<!DOCTYPE html>


<html lang="zh-CN">


<head>
  <meta charset="utf-8" />
    
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
  <title>
     
  </title>
  <meta name="generator" content="hexo-theme-ayer">
  
  <link rel="shortcut icon" href="/favicon.ico" />
  
  
<link rel="stylesheet" href="/dist/main.css">

  
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css">

  
<link rel="stylesheet" href="/css/custom.css">

  
  
<script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>

  
  

  

</head>

</html>

<body>
  <div id="app">
    
      
    <main class="content on">
      
<section class="cover">
    
      
      <a class="forkMe" href="https://github.com/Shen-Yu/hexo-theme-ayer"
        target="_blank"><img width="149" height="149" src="/images/forkme.png"
          class="attachment-full size-full" alt="Fork me on GitHub" data-recalc-dims="1"></a>
    
  <div class="cover-frame">
    <div class="bg-box">
      <img src="/images/cover1.jpg" alt="image frame" />
    </div>
    <div class="cover-inner text-center text-white">
      <h1><a href="/">Hexo</a></h1>
      <div id="subtitle-box">
        
        <span id="subtitle"></span>
        
      </div>
      <div>
        
      </div>
    </div>
  </div>
  <div class="cover-learn-more">
    <a href="javascript:void(0)" class="anchor"><i class="ri-arrow-down-line"></i></a>
  </div>
</section>



<script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js"></script>


<!-- Subtitle -->

  <script>
    try {
      var typed = new Typed("#subtitle", {
        strings: ['面朝大海，春暖花开', '何来天才，唯有苦练', '集中一点，登峰造极'],
        startDelay: 0,
        typeSpeed: 200,
        loop: true,
        backSpeed: 100,
        showCursor: true
      });
    } catch (err) {
      console.log(err)
    }
  </script>
  
<div id="main">
  <section class="outer">
  
  

<div class="notice" style="margin-top:50px">
    <i class="ri-heart-fill"></i>
    <div class="notice-content" id="broad"></div>
</div>
<script type="text/javascript">
    fetch('https://v1.hitokoto.cn')
        .then(response => response.json())
        .then(data => {
            document.getElementById("broad").innerHTML = data.hitokoto;
        })
        .catch(console.error)
</script>

<style>
    .notice {
        padding: 20px;
        border: 1px dashed #e6e6e6;
        color: #969696;
        position: relative;
        display: inline-block;
        width: 100%;
        background: #fbfbfb50;
        border-radius: 10px;
    }

    .notice i {
        float: left;
        color: #999;
        font-size: 16px;
        padding-right: 10px;
        vertical-align: middle;
        margin-top: -2px;
    }

    .notice-content {
        display: initial;
        vertical-align: middle;
    }
</style>
  
  <article class="articles">
    
    
    
    
    <article
  id="post-develop/GitHub 上开源的 RBAC 权限管理系统"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/GitHub%20%E4%B8%8A%E5%BC%80%E6%BA%90%E7%9A%84%20RBAC%20%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/"
    >GitHub 上开源的 RBAC 权限管理系统.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/GitHub%20%E4%B8%8A%E5%BC%80%E6%BA%90%E7%9A%84%20RBAC%20%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="GitHub-上开源的-RBAC-权限管理系统"><a href="#GitHub-上开源的-RBAC-权限管理系统" class="headerlink" title="GitHub 上开源的 RBAC 权限管理系统"></a>GitHub 上开源的 RBAC 权限管理系统</h2><p>原创 江南一点雨 江南一点雨 <em>今天</em></p>
<p>松哥原创的 Spring Boot 视频教程已经杀青，感兴趣的小伙伴戳这里–&gt;<a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzI1NDY0MTkzNQ==&mid=2247489641&idx=1&sn=4cee9122a2fa2677bdc71abf5c7e8c00&scene=21#wechat_redirect">Spring Boot+Vue+微人事视频教程</a></p>
<hr>
<p>前两天写了一篇文章，和大家大概聊了聊 RBAC 权限管理系统：</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MzI1NDY0MTkzNQ==&mid=2247490164&idx=1&sn=664cd4d9f29769e1a5a2748da70f08a2&scene=21#wechat_redirect">Spring Security 中最流行的权限管理模型！</a></li>
</ul>
<p>RBAC 理论其实并不难，相信大家看完文章都能明白。但是如何将理论转为实践代码，这还需要一点功力。</p>
<p>大家可能在网上也会见到众多自称 RBAC 的权限管理系统，这些系统有的确实是 RBAC，有的虽然自称 RBAC，其实并不是。</p>
<p>为了帮助小伙伴掌握 RBAC 权限管理模型，松哥经过大量的摸排，找到了一些开源的 RBAC 权限管理系统，我想写几篇文章和大家分析一下这些系统的实现，搞懂了别人的实现逻辑，自己再去做就非常容易了。</p>
<p>今天要和大家介绍的是一个来自 GitHub 上的项目，叫做 wetech-admin。</p>
<h2 id="1-项目介绍"><a href="#1-项目介绍" class="headerlink" title="1.项目介绍"></a>1.项目介绍</h2><p>wetech-admin 是基于 Spring Boot 2.0+Mybatis+Vue 的轻量级后台管理系统，适用于中小型项目的管理后台，支持按钮级别的权限控制，系统具有最基本的用户管理、角色管理、权限管理等通用性功能，企业或个人可直接在此基础上进行开发，扩展，添加各自的需求和业务功能!</p>
<ul>
<li>项目地址：<a target="_blank" rel="noopener" href="https://github.com/cjbi/wetech-admin">https://github.com/cjbi/wetech-admin</a></li>
</ul>
<p>松哥看了下这个项目，基本上是满足 RBAC0 模型，功能上比较完整，唯一有点遗憾的是，它是用 Shiro 开发的，而不是 Spring Security。</p>
<p>不过这个并不影响我们学习 RBAC，RBAC 作为一种权限模型是语言无关的，无论你用什么语言都可以实现 RBAC，更不用说同一种语言的不同框架了。</p>
<p>只要我们把这个 RBAC 搞懂，用 Spring Security 实现 RBAC 自然也不在话下。其实 Spring Security 实现 RBAC 所需要的技术，在松哥本系列的文章中基本上都已经介绍过了，现在只要大家搞懂了模型，就能很快实现。</p>
<p>我们来看看这个项目的几个效果图：</p>
<p><img src="http://iubest.gitee.io/pic/640.png" alt="img"></p>
<p>可以看到，用户列表、角色列表、权限列表，三个菜单涉及到了权限管理中三张表。</p>
<p><img src="http://iubest.gitee.io/pic/640.png" alt="img">用户管理</p>
<p><img src="http://iubest.gitee.io/pic/640.png" alt="img">角色管理</p>
<p><img src="http://iubest.gitee.io/pic/640.png" alt="img">权限管理</p>
<h2 id="2-项目部署"><a href="#2-项目部署" class="headerlink" title="2.项目部署"></a>2.项目部署</h2><ol>
<li>通过 git 下载源码</li>
<li>创建数据库 wetech_admin，数据库编码为UTF-8</li>
<li>依次执行 src/main/resources/schema.sql 和 dsrc/main/resources/data.sql 文件，初始化数据</li>
<li>修改 application-dev.properties 文件，更新 MySQL 账号和密码</li>
<li>启动服务，账号密码：admin/123456 或者 guest/123456</li>
</ol>
<p>这是官方给出的部署步骤，松哥再详细说两句。</p>
<p>首先项目下载下来后，包含两个目录：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601000046938.png" alt="img"></p>
<ul>
<li>wetech-admin-server：服务端项目，但是由于存在一个 parent，所以不能直接打开 wetech-admin-server，从它的上级目录处打开。</li>
<li>wetech-admin-ui：前端项目，需要首先执行 npm install 安装所需依赖，然后执行 npm run serve 启动前端项目。</li>
</ul>
<p>没搞过前后端分离的小伙伴注意，需要前后端同时启动才能访问。</p>
<p>松哥在启动前端时遇到一个问题，就是一直报这个错误：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Cannot read property &#39;range&#39; of null </span><br></pre></td></tr></table></figure>

<p>检查过后，发现是 eslint 作怪，我就把 eslint 整个关闭了，然后就清净了。</p>
<p>因为我们这里主要是看 RBAC，前端不做过多涉及，项目跑起来就行。如果大家也遇到这个错误，可以按照松哥的办法试下。</p>
<p>另外需要注意，他这项目用了 Lombok，随意 idea 上需要安装一下 Lombok 插件。</p>
<h2 id="3-数据库分析"><a href="#3-数据库分析" class="headerlink" title="3.数据库分析"></a>3.数据库分析</h2><p>这个项目数据库只有三张表：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601000046937.png" alt="img"></p>
<p>权限表、角色表以及用户表。</p>
<p>他没有设计关联表，而是将关联数据做成了一个字段。</p>
<p>比如角色表中有一个 permission_ids 字段，表示该角色对应的权限：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601365931992.png" alt="img"></p>
<p>用户表中有一个 role_ids 字段，表示用户对应的角色：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601000046939.png" alt="img"></p>
<p>这样设计的好处很明显，就是查询的时候非常方便，不用做一对多查询，SQL 好写并且执行效率高。</p>
<p>缺点就是数据更新不太方便，不过一般来说，权限系统数据更新频率较低，大部分都是查询，所以我觉得这样设计也能接受。</p>
<p>我们来看下这里的权限表：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601000046938.png" alt="img"></p>
<p>type 表示资源的类型，分为两种：菜单和按钮。</p>
<p>其中 1 表示菜单，2 表示按钮。如果是菜单，则需要有一个动态路由字符串，也就是 config 字段。该字段返回到前端之后，前端就会动态加载出对应的页面（这点和松哥 vhr （<a target="_blank" rel="noopener" href="https://github.com/lenve/vhr%EF%BC%89%E6%98%AF%E4%B8%80%E8%87%B4%E7%9A%84%EF%BC%89%E3%80%82">https://github.com/lenve/vhr）是一致的）。</a></p>
<p>角色表和用户表都比较简单，我就不再多说。</p>
<h2 id="4-代码分析"><a href="#4-代码分析" class="headerlink" title="4.代码分析"></a>4.代码分析</h2><p>普通的 CURD 代码我就不说了，和权限管理相关的代码在 tech.wetech.admin.shiro 包里边。登录在 LoginController 里边。</p>
<p>先来说这个登录，具体的操作是在 tech.wetech.admin.service.impl.UserServiceImpl#login 方法中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">@Override</span><br><span class="line">public UserTokenDTO login(LoginDTO loginDTO) &#123;</span><br><span class="line">    User user &#x3D; userMapper.selectByUsername(loginDTO.getUsername());</span><br><span class="line">    if (user &#x3D;&#x3D; null) &#123;</span><br><span class="line">        throw new BusinessException(CommonResultStatus.LOGIN_ERROR, &quot;用户不存在&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    if (!passwordHelper.verifyPassword(user, loginDTO.getPassword())) &#123;</span><br><span class="line">        throw new BusinessException(CommonResultStatus.LOGIN_ERROR, &quot;密码错误&quot;);</span><br><span class="line">    &#125;</span><br><span class="line">    UserTokenDTO userInfoDTO &#x3D; new UserTokenDTO();</span><br><span class="line">    userInfoDTO.setUsername(user.getUsername());</span><br><span class="line">    userInfoDTO.setToken(JwtUtil.sign(user.getUsername(), String.valueOf(System.currentTimeMillis())));</span><br><span class="line">    return userInfoDTO;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>这就是纯手工登录，登录成功后，生成一个 jwt 字符串返回给前端，以后前端每次请求，都需要携带上这个 jwt 字符串来。</p>
<p>tech.wetech.admin.shiro.JwtFilter 是专门处理认证请求的过滤器，所有需要认证的请求，都会经过该过滤器。这个过滤器所做的事情，就是从所有的请求头中提取出 Access-Token（就是登录成功后返回的那个 jwt 令牌），然后从令牌中解析出用户名，调用 JwtRealm 完成校验。</p>
<p>这是 JwtFilter：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line">@Slf4j</span><br><span class="line">public class JwtFilter extends AccessControlFilter &#123;</span><br><span class="line"></span><br><span class="line">    protected static final String AUTHORIZATION_HEADER &#x3D; &quot;Access-Token&quot;;</span><br><span class="line"></span><br><span class="line">    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) &#123;</span><br><span class="line">        HttpServletRequest req &#x3D; (HttpServletRequest) request;</span><br><span class="line">        String authorization &#x3D; req.getHeader(AUTHORIZATION_HEADER);</span><br><span class="line">        return authorization !&#x3D; null;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception &#123;</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception &#123;</span><br><span class="line">        HttpServletRequest req &#x3D; (HttpServletRequest) request;</span><br><span class="line">        if (isLoginAttempt(request, response)) &#123;</span><br><span class="line">            &#x2F;&#x2F;生成jwt token</span><br><span class="line">            JwtToken token &#x3D; new JwtToken(req.getHeader(AUTHORIZATION_HEADER));</span><br><span class="line">            &#x2F;&#x2F;委托给Realm进行验证</span><br><span class="line">            try &#123;</span><br><span class="line">                getSubject(request, response).login(token);</span><br><span class="line">                return true;</span><br><span class="line">            &#125; catch (Exception e) &#123;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        onLoginFail(response);</span><br><span class="line">        return false;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    &#x2F;**</span><br><span class="line">     * 登录失败时默认返回401状态码</span><br><span class="line">     *</span><br><span class="line">     * @param response</span><br><span class="line">     * @throws IOException</span><br><span class="line">     *&#x2F;</span><br><span class="line">    private void onLoginFail(ServletResponse response) throws IOException &#123;</span><br><span class="line">        HttpServletResponse httpResponse &#x3D; (HttpServletResponse) response;</span><br><span class="line">        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);</span><br><span class="line">        httpResponse.setContentType(&quot;application&#x2F;json;charset&#x3D;utf-8&quot;);</span><br><span class="line">        httpResponse.getWriter().write(JSONUtil.toJSONString(Result.failure(CommonResultStatus.LOGIN_ERROR)));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>JwtRealm 如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line">@Slf4j</span><br><span class="line">public class JwtRealm extends AuthorizingRealm &#123;</span><br><span class="line"></span><br><span class="line">    @Autowired</span><br><span class="line">    private UserService userService;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    public boolean supports(AuthenticationToken token) &#123;</span><br><span class="line">        return token instanceof JwtToken;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) &#123;</span><br><span class="line">        String username &#x3D; principals.toString();</span><br><span class="line">        SimpleAuthorizationInfo authorizationInfo &#x3D; new SimpleAuthorizationInfo();</span><br><span class="line">        authorizationInfo.setRoles(userService.queryRoles(username));</span><br><span class="line">        authorizationInfo.setStringPermissions(userService.queryPermissions(username));</span><br><span class="line">        return authorizationInfo;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    @Override</span><br><span class="line">    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException &#123;</span><br><span class="line">        String credentials &#x3D; (String) token.getCredentials();</span><br><span class="line">        String username &#x3D; null;</span><br><span class="line">        try &#123;</span><br><span class="line">            boolean verify &#x3D; JwtUtil.verify(credentials);</span><br><span class="line">            if (!verify) &#123;</span><br><span class="line">                throw new AuthenticationException(&quot;Token校验不正确&quot;);</span><br><span class="line">            &#125;</span><br><span class="line">            username &#x3D; JwtUtil.getClaim(credentials, JwtUtil.ACCOUNT);</span><br><span class="line">        &#125; catch (Exception e) &#123;</span><br><span class="line">            log.error(&quot;Token校验不正确：&quot;, e);</span><br><span class="line">            throw new AuthenticationException(e.getMessage());</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        &#x2F;&#x2F;交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配，不设置则使用默认的SimpleCredentialsMatcher</span><br><span class="line">        SimpleAuthenticationInfo authenticationInfo &#x3D; new SimpleAuthenticationInfo(</span><br><span class="line">                username, &#x2F;&#x2F;用户名</span><br><span class="line">                credentials, &#x2F;&#x2F;凭证</span><br><span class="line">                getName()  &#x2F;&#x2F;realm name</span><br><span class="line">        );</span><br><span class="line">        return authenticationInfo;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>认证逻辑松哥刚才已经说过了，就不再赘述。</p>
<p>授权逻辑就是根据用户名查询出用户角色，根据用户角色查询出用户权限，将角色和权限分别赋值给 SimpleAuthorizationInfo 对象即可。</p>
<p>最后在 ShiroConfig 中还有一些常规配置，我就不再多说。</p>
<p>权限的控制主要是在各个接口上添加 @RequiresPermissions 注解实现的。如 PermissionController、RoleController 以及 UserController。</p>
<h2 id="5-小结"><a href="#5-小结" class="headerlink" title="5.小结"></a>5.小结</h2><p>整体上来说，这个项目并没有太多难度。还是松哥一开始所说的，RBAC 只是一种权限设计模型，模型本身很好理解，涉及到的技术也都很简单。无论是 Shiro 还是 Spring Security，实现 RBAC 虽然有一些差别，但是用的都是最基本的技术点。</p>
<p>好啦，今天就和小伙伴们聊这么多，感兴趣的小伙伴可以看看哦～如果你有好的 RBAC 项目也可以留言告诉松哥，我抽空解析一波。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/PyCharm 中文指南"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/PyCharm%20%E4%B8%AD%E6%96%87%E6%8C%87%E5%8D%97/"
    >PyCharm 中文指南.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/PyCharm%20%E4%B8%AD%E6%96%87%E6%8C%87%E5%8D%97/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="PyCharm-中文指南"><a href="#PyCharm-中文指南" class="headerlink" title="PyCharm 中文指南"></a>PyCharm 中文指南</h1><p>大家好，我是明哥。</p>
<p>《PyCharm 中文指南》 里有大量的 GIF 动态演示图，由于 PDF 是直接 Markdown 渲染生成的，GIF 就变成了静态图，想要有更好的阅读体验，可以去在线网址:<em><a target="_blank" rel="noopener" href="http://pycharm.iswbm.com/">http://pycharm.iswbm.com</a></em></p>
<p>《PyCharm 中文指南》在近期内会一直保持更新，如果大家有不错的使用技巧欢迎分享给我，一起维护这本书，我相信在未来它会在互联网传播开来，影响越来越多的人。你可以通过后台留言，或者加我微信都是可以的。</p>
<p>昨天有相当多的同学找我领取『便携版』绿色免安装的 2020.2 专业版 PyCharm， 但还不知道怎么使用，我就借这篇文章也介绍一下。</p>
<p>需要的赶紧跟着操作，现在不需要的赶紧备份，因为过两天这文章就会删除。</p>
<p><strong>Windows 怎么破？</strong></p>
<p>首先从你的百度网盘把我给你的 exe 文件下载下来。</p>
<p>下载后，你会得到一个名为 Pycharm_2020.2_Portable.exe 的可执行文件。双击它，会弹出如下的界面，让你选择文件的解压路径</p>
<p>解压完成后，请先清空这个目录（如果没有这个路径，跳过即可），避免出现无法激活的情况：C:\Users\你的用户名\AppData\RoamingJetBrains.PyCharm 2020.2 。</p>
<p>然后进入 D:Program FilesJetBrainsPycharm_2020.2_Portablein 目录，点击 pycharm64.exe 打开软件，为了后续方便，你可以右键发送快捷方式到桌面。</p>
<p>打开后，无需任何样式配置，直接就进入了软件，简直不要太省心。</p>
<p>点击 Help -&gt; About 查看一下激活信息，真的激活到 2089 了，从此以后就不用再担心过期了。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/Python 中操作常见的配置文件"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/Python%20%E4%B8%AD%E6%93%8D%E4%BD%9C%E5%B8%B8%E8%A7%81%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/"
    >Python 中操作常见的配置文件.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/Python%20%E4%B8%AD%E6%93%8D%E4%BD%9C%E5%B8%B8%E8%A7%81%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Python-中操作常见的配置文件"><a href="#Python-中操作常见的配置文件" class="headerlink" title="Python 中操作常见的配置文件"></a>Python 中操作常见的配置文件</h1><h3 id="为什么要写配置文件"><a href="#为什么要写配置文件" class="headerlink" title="为什么要写配置文件"></a>为什么要写配置文件</h3><p>在开发过程中，我们常常会用到一些固定参数或者是常量。对于这些较为固定且常用到的部分，往往会将其写到一个固定文件中，避免在不同的模块代码中重复出现从而保持核心代码整洁。这个固定文件我们可以直接写成一个 <code>.py</code> 文件，例如 <code>settings.py</code> 或 <code>config.py</code>，这样的好处就是能够在同一工程下直接通过 <code>import</code> 来导入当中的部分；但如果我们需要在其他<strong>非 Python 的平台</strong>进行配置文件共享时，写成单个 <code>.py</code> 就不是一个很好的选择。这时我们就应该选择通用的配置文件类型来作为存储这些固定的部分。目前常用且流行的配置文件格式类型主要有 <code>ini</code>、<code>json</code>、<code>toml</code>、<code>yaml</code>、<code>xml</code> 等，这些类型的配置文件我们都可以通过标准库或第三方库来进行解析。</p>
<h3 id="ini"><a href="#ini" class="headerlink" title="ini"></a>ini</h3><p><code>ini</code> 即 Initialize 初始化之意，早期是在 Windows 上配置文件的存储格式。<code>ini</code> 文件的写法通俗易懂，往往比较简单，通常由节（Section）、键（key）和值（value）组成，就像以下形式：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">\[localdb\]    </span><br><span class="line">host     &#x3D; 127.0.0.1    </span><br><span class="line">user     &#x3D; root    </span><br><span class="line">password &#x3D; 123456    </span><br><span class="line">port     &#x3D; 3306    </span><br><span class="line">database &#x3D; mysql    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Python 本身内置的 <code>configparser</code> 标准库，我们直接就可以用来对 <code>ini</code> 文件进行解析。如我们将上述内容保存在一个名为 <code>db.ini</code> 的文件中，然后使用 <code>read()</code> 方法来进行解析和读取，最后通过 <code>items()</code> 方法来获取指定节点下的所有键值对。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">\&gt;&gt;&gt; from configparser import ConfigParser    </span><br><span class="line">\&gt;&gt;&gt; cfg &#x3D; ConfigParser()    </span><br><span class="line">\&gt;&gt;&gt; cfg.read(&quot;&#x2F;Users&#x2F;Bobot&#x2F;db.ini&quot;)    </span><br><span class="line">\[&#39;&#x2F;Users&#x2F;Bobot&#x2F;db.ini&#39;\]    </span><br><span class="line">\&gt;&gt;&gt; cfg.items(&quot;localdb&quot;)    </span><br><span class="line">\[(&#39;host&#39;, &#39;127.0.0.1&#39;), (&#39;user&#39;, &#39;root&#39;), (&#39;password&#39;, &#39;123456&#39;), (&#39;port&#39;, &#39;3306&#39;), (&#39;database&#39;, &#39;mysql&#39;)\]    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>需要注意的是，<code>configparser</code> 默认将值<strong>以字符串的形式</strong>呈现，所以这也就是为什么我们在 <code>db.ini</code> 文件中没有加引号而是直接将字面量写在上面的原因。获取到键值对后，我其实直接就将其转换成字典，然后通过解包的方式进行穿参，保持代码简洁：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">#!pip install pymysql    </span><br><span class="line">import pymysql    </span><br><span class="line">from configparser import ConfigParser    </span><br><span class="line">    </span><br><span class="line">cfg &#x3D; ConfigParser()    </span><br><span class="line">cfg.read(&quot;&#x2F;Users&#x2F;Bobot&#x2F;db.ini&quot;)    </span><br><span class="line">db\_cfg &#x3D; dict(cfg.items(&quot;localdb&quot;))    </span><br><span class="line">    </span><br><span class="line">con &#x3D; pymysql.connect(\*\*db\_cfg)    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="json"><a href="#json" class="headerlink" title="json"></a>json</h3><p><code>json</code> 格式可以说是我们常见的一种文件形式了，也是目前在互联网较为流行的一种数据交换格式。除此之外，<code>json</code> 有时也是配置文件的一种。比如 <code>npm</code>（JavaScript 包管理工具类似 Python 的 <code>pip</code>）、以及微软出品的目前被广泛使用的 VSCode 编辑器，都使用 <code>json</code> 编写配置参数。和 <code>configparser</code> 一样，Python 也内置了 <code>json</code> 标准库，可以通过 <code>load()</code> 和 <code>loads()</code> 方法来导入文件式和字符串的 <code>json</code> 内容。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">&#123;    </span><br><span class="line">    &quot;localdb&quot;:&#123;    </span><br><span class="line">        &quot;host&quot;: &quot;127.0.0.1&quot;,    </span><br><span class="line">        &quot;user&quot;: &quot;root&quot;,    </span><br><span class="line">        &quot;password&quot;: &quot;123456&quot;,    </span><br><span class="line">        &quot;port&quot;: 3306,    </span><br><span class="line">        &quot;database&quot;: &quot;mysql&quot;    </span><br><span class="line">    &#125;    </span><br><span class="line">&#125;      </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>我们将上述内容保存为 <code>db.json</code> 后进行读取和解析，<code>json</code> 库读取 json 文件相对简单容易，而且很容易解析成 Python 的字典对象。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">\&gt;&gt;&gt; import json    </span><br><span class="line">\&gt;&gt;&gt; from pprint import pprint    </span><br><span class="line">\&gt;&gt;&gt;     </span><br><span class="line">\&gt;&gt;&gt; with open(&#39;&#x2F;Users&#x2F;Bobot&#x2F;db.json&#39;) as j:    </span><br><span class="line">...     cfg &#x3D; json.load(j)\[&#39;localdb&#39;\]    </span><br><span class="line">...     </span><br><span class="line">\&gt;&gt;&gt; pprint(cfg)    </span><br><span class="line">&#123;&#39;database&#39;: &#39;mysql&#39;,    </span><br><span class="line"> &#39;host&#39;: &#39;127.0.0.1&#39;,    </span><br><span class="line"> &#39;password&#39;: &#39;123456&#39;,    </span><br><span class="line"> &#39;port&#39;: 3306,    </span><br><span class="line"> &#39;user&#39;: &#39;root&#39;&#125;    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>使用 <code>json</code> 文件配置的缺点就是语法标准严格限制，为人所诟病之一的就是<strong>无法在当中写注释</strong>，除非采取 <code>json</code> 类型的其他<strong>超集</strong>作为替代方案（VSCode 中能写注释的 <code>json</code> 参数配置文件便是代替方案的一种）；同时存在<strong>嵌套过深</strong>的问题，容易导致出错，不宜用来写过长或复杂的参数配置信息。</p>
<h3 id="toml"><a href="#toml" class="headerlink" title="toml"></a>toml</h3><p><code>toml</code> 格式（或 <code>tml</code> 格式）是 Github 联合创始人 Tom Preston-Werner 所提出的一种配置文件格式。根据维基百科的资料，<code>toml</code> 最开始提出时是在 2013年7月份，距今已有七年时间；它在某些方面也与后面要谈到的 <code>yaml</code> 文件有些类似，但如果当你知道 yaml 的规范有几十页（没有错，真的就是几十页……）的时候，可能你真的就不太愿意去写那么复杂的配置文件，<code>toml</code> 格式则倒是个不错的选择。<code>toml</code> 格式大致如下：</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/5mt0ewv9OS3n49UQqicXusCZzrNGgd6MJPcLM1yTZsL16mQBWEebp52usyuMjZH00mx5uJVEwsiaOsvgVmLXGjnw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p>从这里可以看出 <code>toml</code> 有点类似于前面所讲的 <code>ini</code> 文件。但是它比 <code>ini</code> 扩展了更多的内容。在样例图片中我们可以看到，除了基本的字符串以外，例如时间戳、布尔值、数组等都进一步支持，而且样式和 Python 的原生写法十分类似。当然这里不会过多介绍 <code>toml</code> 格式的一些规范说明，有人已经对官方的规范文档进行了翻译，有兴趣的朋友可以直接查阅。这么契合 Python 方式的配置文件类型已经有开发者造出了相应的「轮子」，目前在 Github 上 Stars 数最多的是则 <code>uiri/toml</code> 的版本，不过该版本仅通过了 v0.5 版本 <code>toml</code> 规范，但在使用上还是蛮简洁的，我们可以通过 <code>pip</code> 命令进行安装</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">pip install toml    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>该库的解析方式很简单，也有点类似于 <code>json</code> 库的解析用法，即通过<code>load()</code> 或 <code>loads()</code> 来进行解析；同理转换并导出也是同样类似的用法。比如我们现在将以下内容写入到 <code>config.toml</code> 中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">\[mysql\]    </span><br><span class="line">host     &#x3D; &quot;127.0.0.1&quot;    </span><br><span class="line">user     &#x3D; &quot;root&quot;    </span><br><span class="line">port     &#x3D; 3306    </span><br><span class="line">database &#x3D; &quot;test&quot;    </span><br><span class="line">    </span><br><span class="line">  \[mysql.parameters\]    </span><br><span class="line">  pool\_size &#x3D; 5    </span><br><span class="line">  charset   &#x3D; &quot;utf8&quot;    </span><br><span class="line">    </span><br><span class="line">  \[mysql.fields\]    </span><br><span class="line">  pandas\_cols &#x3D; \[ &quot;id&quot;, &quot;name&quot;, &quot;age&quot;, &quot;date&quot;\]    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>紧接着我们就可以通过 <code>toml</code> 库中的 <code>load()</code> 方法来进行读取：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">\&gt;&gt;&gt; import toml    </span><br><span class="line">\&gt;&gt;&gt; import os    </span><br><span class="line">\&gt;&gt;&gt; from pprint import pprint    </span><br><span class="line">\&gt;&gt;&gt; cfg &#x3D; toml.load(os.path.expanduser(&quot;~&#x2F;Desktop&#x2F;config.toml&quot;))    </span><br><span class="line">\&gt;&gt;&gt; pprint(cfg)    </span><br><span class="line">&#123;&#39;mysql&#39;: &#123;&#39;database&#39;: &#39;test&#39;,    </span><br><span class="line">           &#39;fields&#39;: &#123;&#39;pandas\_cols&#39;: \[&#39;id&#39;, &#39;name&#39;, &#39;age&#39;, &#39;date&#39;\]&#125;,    </span><br><span class="line">           &#39;host&#39;: &#39;127.0.0.1&#39;,    </span><br><span class="line">           &#39;parameters&#39;: &#123;&#39;charset&#39;: &#39;utf8&#39;, &#39;pool\_size&#39;: 5&#125;,    </span><br><span class="line">           &#39;port&#39;: 3306,    </span><br><span class="line">           &#39;user&#39;: &#39;root&#39;&#125;&#125;    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>可以看到 <code>toml</code> 文件被间接地转化成了字典类型，当然这也就是 <code>json</code> 版的写法（将单引号替换成双引号即可），方便我们后续调用或者传参。</p>
<h3 id="yaml"><a href="#yaml" class="headerlink" title="yaml"></a>yaml</h3><p><code>yaml</code> 格式（或 <code>yml</code> 格式）是目前较为流行的一种配置文件，它早在 2001 由一个名为 Clark Evans 的人提出；同时它也是目前被广泛使用的配置文件类型，典型的就是 Docker 容器里的 <code>docker-compose.yml</code> 配置文件，如果经常使用 Docker 进行部署的人对此不会陌生。<code>yaml</code> 文件的设计从 Python、XML 等地方获取灵感，所以在使用时能很清楚地看到这些部分的影子。在上一节 <code>toml</code> 内容里我曾提到，<code>yaml</code> 的规范内容可以说是冗长和复杂，足足有80页之多（斗尊强者，恐怖如斯……）。</p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/5mt0ewv9OS3n49UQqicXusCZzrNGgd6MJIqUFBT08EqLEhgicHuib4cyVyhTPPicWbLZRwnia0sOgQP4r7Y0SuK36lg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1">02-yaml规范页数</p>
<p>所以感兴趣的朋友可以再自行了解相关用法。YAML 官方早已经提供了相应的 Python 库进行支持，即 <code>PyYAML</code>；当然也同样需要我们事先进行安装：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">pip install pyyaml    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>同 <code>json</code> 库和 <code>toml</code> 库一样，通过 <code>load()</code> 方法来进行加载。需要注意的是，使用 <code>load()</code> 方法<strong>会存在一定的安全隐患</strong>，从思科 Talos 的这份报告中我们可以看到，如果加载了未知或不信任的 <code>yaml</code> 文件，那么有可能会存在被攻击的风险和网络安全隐患，因为它能够直接调用相应的 Python 函数来执行为攻击者所需要的命令，比如说在 <code>yaml</code> 文件中写入这么一段：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line"># 使用Linux和macOS的朋友不要轻易尝试    </span><br><span class="line">!!python&#x2F;object&#x2F;apply:os.system \[&quot;rm -rf &#x2F;&quot;\]    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>因此最好是使用 <code>safe_load()</code> 来代替 <code>load()</code> 方法。这和 Python 内置的 <code>string</code> 标准库中 <code>Template</code> 类的 <code>substitute()</code> 模板方法一样存在着同样的安全隐患，所以使用 <code>safe_substitute()</code> 来替代是一样的道理。如我们现在将之前的一些配置信息写入 <code>config.yaml</code> 文件中：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">mysql:    </span><br><span class="line">  host: &quot;127.0.0.1&quot;    </span><br><span class="line">  port: 3306    </span><br><span class="line">  user: &quot;root&quot;    </span><br><span class="line">  password: &quot;123456&quot;    </span><br><span class="line">  database: &quot;test&quot;    </span><br><span class="line">    </span><br><span class="line">  parameter:    </span><br><span class="line">    pool\_size: 5    </span><br><span class="line">    charset: &quot;utf8&quot;    </span><br><span class="line">    </span><br><span class="line">  fields:    </span><br><span class="line">    pandas\_cols:     </span><br><span class="line">      - id    </span><br><span class="line">      - name    </span><br><span class="line">      - age    </span><br><span class="line">      - date    </span><br><span class="line">    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>然后我们通过 <code>safe_load()</code> 方法进行解析：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"></span><br><span class="line">\&gt;&gt;&gt; import os    </span><br><span class="line">\&gt;&gt;&gt; from pprint import pprint    </span><br><span class="line">\&gt;&gt;&gt;     </span><br><span class="line">\&gt;&gt;&gt; with open(os.path.expanduser(&quot;~&#x2F;config.yaml&quot;), &quot;r&quot;) as config:    </span><br><span class="line">...     cfg &#x3D; yaml.safe\_load(config)    </span><br><span class="line">...     </span><br><span class="line">\&gt;&gt;&gt; pprint(cfg)    </span><br><span class="line">&#123;&#39;mysql&#39;: &#123;&#39;database&#39;: &#39;test&#39;,    </span><br><span class="line">           &#39;fields&#39;: &#123;&#39;pandas\_cols&#39;: \[&#39;id&#39;, &#39;name&#39;, &#39;age&#39;, &#39;date&#39;\]&#125;,    </span><br><span class="line">           &#39;host&#39;: &#39;127.0.0.1&#39;,    </span><br><span class="line">           &#39;parameter&#39;: &#123;&#39;charset&#39;: &#39;utf8&#39;, &#39;pool\_size&#39;: 5&#125;,    </span><br><span class="line">           &#39;password&#39;: &#39;123456&#39;,    </span><br><span class="line">           &#39;port&#39;: 3306,    </span><br><span class="line">           &#39;user&#39;: &#39;root&#39;&#125;&#125;    </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>可以看到最后结果和前面的 <code>toml</code> 库的解析结果基本一致。</p>
<h3 id="结尾"><a href="#结尾" class="headerlink" title="结尾"></a>结尾</h3><p>本文列举了一些主流且常见的配置文件类型及其 Python 的读取方法，可能有的读者会发现当中没有 <code>xml</code> 格式类型的内容。对于 <code>xml</code> 配置文件可能与 Java 系语言打交道的朋友遇见得会多一些，但 <code>xml</code> 文件的可读性实在是让人望而生畏；对 <code>xml</code> 文件不了解的朋友可以使用 Chrome 浏览器随便进入一个网站然后按下 F12 进入开发者后查看那密密麻麻的 html 元素便是 <code>.xml</code> 的缩影。除了这些主流的配置文件类型之外，像一些 <code>.cfg</code>、<code>.properties</code> 等都可以作为配置文件，甚至和开头提到的那样，你单独用一个 <code>.py</code> 文件来书写各类配置信息作为配置文件进行导入都是没问题，只是在跨语言共享时可能会有些障碍。因此本文就不过多介绍，感兴趣的朋友可以进一步自行了解。在本文里列举的配置文件类型其复杂性由上到下依次增加：<code>ini &lt; json ≈ toml &lt; yaml</code>，它们之间各有优劣，可以根据自己实际的需求和团队协作要求来具体选择。</p>
<p><em>文章来自：Python中文社区</em>  </p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/QFzRdz9libEagf3GqMkG2r8nyTK3msmDpqnUPzT4HyN6wSpwNia24JAcHeF4BmWKotAqs0IibPkl9CZL3Yp05TCyA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/QFzRdz9libEagf3GqMkG2r8nyTK3msmDpd79yRpcwBjt8yQlG1dJ3NNxtSSqicpdEVFk3iax1ia4p4CoENnkTZh7ibA/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/QFzRdz9libEagf3GqMkG2r8nyTK3msmDpIfuyiaAHcXRtjicE7OjbHoHoNMtwgkh6iaAsdFVmrUJ5IEw6zk33KObGg/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p><strong>马哥教育Linux、Python、Go系列课程火热报名中</strong></p>
<p><img src="https://mmbiz.qpic.cn/mmbiz_png/2rMyvdWluHs3Xwo84icpXFn5eVv5jBhILen6C64qU5QpmBcALIbBQb53fRvCXicxrqBqJd7dbQG2hhdkvnGUFNQw/640?wx_fmt=png&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1"></p>
<p>我就知道你“在看”</p>
<p><img src=""></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/Python 炫技操作：模块重载的五种方法"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/Python%20%E7%82%AB%E6%8A%80%E6%93%8D%E4%BD%9C%EF%BC%9A%E6%A8%A1%E5%9D%97%E9%87%8D%E8%BD%BD%E7%9A%84%E4%BA%94%E7%A7%8D%E6%96%B9%E6%B3%95/"
    >Python 炫技操作：模块重载的五种方法.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/Python%20%E7%82%AB%E6%8A%80%E6%93%8D%E4%BD%9C%EF%BC%9A%E6%A8%A1%E5%9D%97%E9%87%8D%E8%BD%BD%E7%9A%84%E4%BA%94%E7%A7%8D%E6%96%B9%E6%B3%95/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h2 id="Python-炫技操作：模块重载的五种方法"><a href="#Python-炫技操作：模块重载的五种方法" class="headerlink" title="Python 炫技操作：模块重载的五种方法"></a>Python 炫技操作：模块重载的五种方法</h2><h2 id="环境准备"><a href="#环境准备" class="headerlink" title="环境准备"></a>环境准备</h2><p>新建一个 foo 文件夹，其下包含一个 bar.py 文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">$ tree foo</span><br><span class="line">foo</span><br><span class="line">└── bar.py</span><br><span class="line"></span><br><span class="line">0 directories, 1 file</span><br></pre></td></tr></table></figure>

<p>bar.py 的内容非常简单，只写了个 print 语句</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">print(&quot;successful to be imported&quot;)</span><br></pre></td></tr></table></figure>

<p>只要 bar.py 被导入一次，就被执行一次 print</p>
<h2 id="禁止重复导入"><a href="#禁止重复导入" class="headerlink" title="禁止重复导入"></a>禁止重复导入</h2><p>由于有 sys.modules 的存在，当你导入一个已导入的模块时，实际上是没有效果的。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">&gt;&gt;&gt;</span><br></pre></td></tr></table></figure>

<h2 id="重载模块方法一"><a href="#重载模块方法一" class="headerlink" title="重载模块方法一"></a>重载模块方法一</h2><p>如果你使用的 python2（记得前面在 foo 文件夹下加一个 <code>__init__.py</code>），有一个 reload 的方法可以直接使用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; reload(bar)</span><br><span class="line">successful to be imported</span><br><span class="line">&lt;module &#39;foo.bar&#39; from &#39;foo&#x2F;bar.pyc&#39;&gt;</span><br></pre></td></tr></table></figure>

<p>如果你使用的 python3 那方法就多了，详细请看下面</p>
<h2 id="重载模块方法二"><a href="#重载模块方法二" class="headerlink" title="重载模块方法二"></a>重载模块方法二</h2><p>如果你使用 Python3.0 -&gt; 3.3，那么可以使用 imp.reload 方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import imp</span><br><span class="line">&gt;&gt;&gt; imp.reload(bar)</span><br><span class="line">successful to be imported</span><br><span class="line">&lt;module &#39;foo.bar&#39; from &#39;&#x2F;Users&#x2F;MING&#x2F;Code&#x2F;Python&#x2F;foo&#x2F;bar.py&#39;&gt;</span><br></pre></td></tr></table></figure>

<p>但是这个方法在 Python 3.4+，就不推荐使用了</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;stdin&gt;:1: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module&#39;s documentation for alternative uses</span><br></pre></td></tr></table></figure>

<h2 id="重载模块方法三"><a href="#重载模块方法三" class="headerlink" title="重载模块方法三"></a>重载模块方法三</h2><p>如果你使用的 Python 3.4+，请使用 importlib.reload 方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import importlib</span><br><span class="line">&gt;&gt;&gt; importlib.reload(bar)</span><br><span class="line">successful to be imported</span><br><span class="line">&lt;module &#39;foo.bar&#39; from &#39;&#x2F;Users&#x2F;MING&#x2F;Code&#x2F;Python&#x2F;foo&#x2F;bar.py&#39;&gt;</span><br></pre></td></tr></table></figure>

<h2 id="重载模块方法四"><a href="#重载模块方法四" class="headerlink" title="重载模块方法四"></a>重载模块方法四</h2><p>如果你对包的加载器有所了解（详细可以翻阅我以前写的文章：<em><a target="_blank" rel="noopener" href="https://iswbm.com/84.html">https://iswbm.com/84.html</a></em>）</p>
<p>还可以使用下面的方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; bar.__spec__.loader.load_module()</span><br><span class="line">successful to be imported</span><br><span class="line">&lt;module &#39;foo.bar&#39; from &#39;&#x2F;Users&#x2F;MING&#x2F;Code&#x2F;Python&#x2F;foo&#x2F;bar.py&#39;&gt;</span><br></pre></td></tr></table></figure>

<h2 id="重载模块方法五"><a href="#重载模块方法五" class="headerlink" title="重载模块方法五"></a>重载模块方法五</h2><p>既然影响我们重复导入的是 sys.modules，那我们只要将已导入的包从其中移除是不是就好了呢？</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; import foo.bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import foo.bar</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import sys</span><br><span class="line">&gt;&gt;&gt; sys.modules[&#39;foo.bar&#39;]</span><br><span class="line">&lt;module &#39;foo.bar&#39; from &#39;&#x2F;Users&#x2F;MING&#x2F;Code&#x2F;Python&#x2F;foo&#x2F;bar.py&#39;&gt;</span><br><span class="line">&gt;&gt;&gt; del sys.modules[&#39;foo.bar&#39;]</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import foo.bar</span><br><span class="line">successful to be imported</span><br></pre></td></tr></table></figure>

<p>有没有发现在前面的例子里我使用的都是 <code>from foo import bar</code>，在这个例子里，却使用 <code>import foo.bar</code>，这是为什么呢？</p>
<p>这是因为如果你使用 <code>from foo import bar</code> 这种方式，想使用移除 sys.modules 来重载模块这种方法是失效的。</p>
<p>这应该算是一个小坑，不知道的人，会掉入坑中爬不出来。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; import foo.bar</span><br><span class="line">successful to be imported</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import foo.bar</span><br><span class="line">&gt;&gt;&gt;</span><br><span class="line">&gt;&gt;&gt; import sys</span><br><span class="line">&gt;&gt;&gt; del sys.modules[&#39;foo.bar&#39;]</span><br><span class="line">&gt;&gt;&gt; from foo import bar</span><br><span class="line">&gt;&gt;&gt;</span><br></pre></td></tr></table></figure> 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/Python装饰器，就是这么强大！"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/Python%E8%A3%85%E9%A5%B0%E5%99%A8%EF%BC%8C%E5%B0%B1%E6%98%AF%E8%BF%99%E4%B9%88%E5%BC%BA%E5%A4%A7%EF%BC%81/"
    >Python装饰器，就是这么强大！.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/Python%E8%A3%85%E9%A5%B0%E5%99%A8%EF%BC%8C%E5%B0%B1%E6%98%AF%E8%BF%99%E4%B9%88%E5%BC%BA%E5%A4%A7%EF%BC%81/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="Python装饰器，就是这么强大"><a href="#Python装饰器，就是这么强大" class="headerlink" title="Python装饰器，就是这么强大"></a>Python装饰器，就是这么强大</h1><h3 id="1-装饰器的定义"><a href="#1-装饰器的定义" class="headerlink" title="1. 装饰器的定义"></a>1. 装饰器的定义</h3><p>就是给已有函数增加额外功能的函数，它本质上就是一个闭包函数。装饰器的功能特点:</p>
<p>1、不修改已有函数的源代码</p>
<p>2、不修改已有函数的调用方式</p>
<p>3、给已有函数增加额外的功能</p>
<p>4、闭包和装饰器的区分：</p>
<p>如果闭包函数的参数有且只有一个，并且是函数类型，那么这个闭包函数称之为装饰器。写代码要遵循开放封闭原则，它规定已经实现的功能代码不允许被修改，但可以被扩展。</p>
<h3 id="2-装饰器的示例代码"><a href="#2-装饰器的示例代码" class="headerlink" title="2. 装饰器的示例代码"></a>2. 装饰器的示例代码</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"># 定义装饰器  </span><br><span class="line">def decorator(func):  </span><br><span class="line">    def inner():  </span><br><span class="line">        # 在内部函数里面对已有函数进行装饰  </span><br><span class="line">        print(&#39;已添加登录认证&#39;)  </span><br><span class="line">        func()  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">def comment():  </span><br><span class="line">    print(&#39;发表评论&#39;)  </span><br><span class="line"># 调用装饰器对已有函数进行装饰，左边的comment&#x3D;inner  </span><br><span class="line">comment &#x3D; decorator(comment)  </span><br><span class="line"># 调用方式不变  </span><br><span class="line">comment()  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="3-装饰器的语法糖写法"><a href="#3-装饰器的语法糖写法" class="headerlink" title="3. 装饰器的语法糖写法"></a>3. 装饰器的语法糖写法</h3><p>如果有多个函数都需要添加登录验证的功能，每次都需要编写func = decorator(func)这样代码对已有函数进行装饰，这种做法还是比较麻烦。</p>
<p>Python给提供了一个装饰函数更加简单的写法，那就是语法糖，语法糖的书写格式是: @装饰器名字，通过语法糖的方式也可以完成对已有函数的装饰。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"># 定义装饰器  </span><br><span class="line">def decorator(func):  </span><br><span class="line">    def inner():  </span><br><span class="line">        # 在内部函数里面对已有函数进行装饰  </span><br><span class="line">        print(&#39;已添加登录认证&#39;)  </span><br><span class="line">        func()  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">@decorator  # comment &#x3D; decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment&#x3D;inner  </span><br><span class="line">def comment():  </span><br><span class="line">    print(&#39;发表评论&#39;)  </span><br><span class="line"># 调用方式不变  </span><br><span class="line">comment()  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="4-装饰器的执行时机"><a href="#4-装饰器的执行时机" class="headerlink" title="4. 装饰器的执行时机"></a>4. 装饰器的执行时机</h3><p>当前模块加载完成以后，装饰器会立即执行，对已有函数进行装饰。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"># 定义装饰器  </span><br><span class="line">def decorator(func):  </span><br><span class="line">    print(&#39;装饰器执行了&#39;)  </span><br><span class="line">    def inner():  </span><br><span class="line">        # 在内部函数里面对已有函数进行装饰  </span><br><span class="line">        print(&#39;已添加登录认证&#39;)  </span><br><span class="line">        func()  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">@decorator  # comment &#x3D; decorator(comment) 装饰器语法糖对该代码进行了封装 左边comment&#x3D;inner  </span><br><span class="line">def comment():  </span><br><span class="line">    print(&#39;发表评论&#39;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="5-装饰器的使用"><a href="#5-装饰器的使用" class="headerlink" title="5. 装饰器的使用"></a>5. 装饰器的使用</h3><h4 id="5-1-装饰器的使用场景"><a href="#5-1-装饰器的使用场景" class="headerlink" title="5.1 装饰器的使用场景"></a>5.1 装饰器的使用场景</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">1.函数执行时间的统计。  </span><br><span class="line">  </span><br><span class="line">2.输出日志信息。  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="5-2-装饰器实现已有函数执行时间的统计"><a href="#5-2-装饰器实现已有函数执行时间的统计" class="headerlink" title="5.2 装饰器实现已有函数执行时间的统计"></a>5.2 装饰器实现已有函数执行时间的统计</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">import time  </span><br><span class="line">  </span><br><span class="line">def decorator(func):  </span><br><span class="line">    def inner():  </span><br><span class="line">        # 获取时间距离1970-1-1 0:0:1的时间差  </span><br><span class="line">        begin &#x3D; time.time()  </span><br><span class="line">        func()  </span><br><span class="line">        end &#x3D; time.time()  </span><br><span class="line">        result &#x3D; end - begin  </span><br><span class="line">        print(f&#39;函数执行完成耗时：&#123;result&#125;&#39;)  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">@decorator  </span><br><span class="line">def work():  </span><br><span class="line">    for i in range(10000):  </span><br><span class="line">        print(i)  </span><br><span class="line">          </span><br><span class="line">work()  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="6-通用装饰器的使用"><a href="#6-通用装饰器的使用" class="headerlink" title="6. 通用装饰器的使用"></a>6. 通用装饰器的使用</h3><p>通用装饰器：可以装饰任意类型的函数。使用装饰器装饰已有函数的时候，内部函数的类型和要装饰的已有函数的类型保持一致。</p>
<h4 id="6-1-装饰带有参数的函数"><a href="#6-1-装饰带有参数的函数" class="headerlink" title="6.1 装饰带有参数的函数"></a>6.1 装饰带有参数的函数</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">def decorator(func):  </span><br><span class="line">    def inner(num1, num2):  </span><br><span class="line">        print(&#39;正在努力执行加法计算&#39;)  </span><br><span class="line">        func(num1, num2)  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">@decorator  </span><br><span class="line">def add_num(num1, num2):  </span><br><span class="line">    result &#x3D; num1 + num2  </span><br><span class="line">    print(f&#39;结果为：&#123;result&#125;&#39;)  </span><br><span class="line">  </span><br><span class="line">add_num(1, 2)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="6-2-装饰带有参数、返回值的函数"><a href="#6-2-装饰带有参数、返回值的函数" class="headerlink" title="6.2 装饰带有参数、返回值的函数"></a>6.2 装饰带有参数、返回值的函数</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">def decorator(func):  </span><br><span class="line">    def inner(num1, num2):  </span><br><span class="line">        print(&#39;正在努力执行加法计算&#39;)  </span><br><span class="line">        num &#x3D; func(num1, num2)  </span><br><span class="line">        return num  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">@decorator  </span><br><span class="line">def add_num(num1, num2):  </span><br><span class="line">    result &#x3D; num1 + num2  </span><br><span class="line">    return result  </span><br><span class="line">  </span><br><span class="line">result &#x3D; add_num(1, 2)  </span><br><span class="line">print(f&#39;结果为：&#123;result&#125;&#39;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="6-3-装饰带有不定长参数、返回值的函数"><a href="#6-3-装饰带有不定长参数、返回值的函数" class="headerlink" title="6.3 装饰带有不定长参数、返回值的函数"></a>6.3 装饰带有不定长参数、返回值的函数</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">def decorator(func):  </span><br><span class="line">    def inner(*args, **kwargs):  </span><br><span class="line">        print(&#39;正在努力执行加法计算&#39;)  </span><br><span class="line">        # *args:把元组里面的每一个元素，按照位置参数的方式进行传参  </span><br><span class="line">        # **kwargs:把字典里面的每一个键值对，按照关键字的方式进行传参  </span><br><span class="line">        num &#x3D; func(*args, **kwargs)  </span><br><span class="line">        return num  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">@decorator  </span><br><span class="line">def add_num(*args, **kwargs):  </span><br><span class="line">    result &#x3D; 0  </span><br><span class="line">    for value in args:  </span><br><span class="line">        result +&#x3D; value  </span><br><span class="line">    for value in kwargs.values():  </span><br><span class="line">        result +&#x3D; value  </span><br><span class="line">    return result  </span><br><span class="line">  </span><br><span class="line">result &#x3D; add_num(1, 2, a&#x3D;3)  </span><br><span class="line">print(f&#39;结果为：&#123;result&#125;&#39;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="7-多个装饰器的使用"><a href="#7-多个装饰器的使用" class="headerlink" title="7. 多个装饰器的使用"></a>7. 多个装饰器的使用</h3><p>多个装饰器的装饰过程：由内到外的一个装饰过程，先执行内部的装饰器，在执行外部的装饰器。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">def make_div(func):  </span><br><span class="line">    print(&#39;make_div装饰器执行了&#39;)  </span><br><span class="line">  </span><br><span class="line">    def inner():  </span><br><span class="line">        result &#x3D; &#39;&lt;div&gt;&#39; + func() + &#39;&lt;&#x2F;div&gt;&#39;  </span><br><span class="line">        return result  </span><br><span class="line">    return inner  </span><br><span class="line">  </span><br><span class="line">def make_p(func):  </span><br><span class="line">    print(&#39;make_p装饰器执行了&#39;)  </span><br><span class="line">  </span><br><span class="line">    def inner():  </span><br><span class="line">        result &#x3D; &#39;&lt;p&gt;&#39; + func() + &#39;&lt;&#x2F;p&gt;&#39;  </span><br><span class="line">        return result  </span><br><span class="line">    return inner  </span><br><span class="line"># 原理剖析：content &#x3D; make_div(make_p(content))  </span><br><span class="line"># 分布拆解：content &#x3D; make_p(content)，内部装饰器完成，content &#x3D; make_p.inner  </span><br><span class="line">#          content &#x3D; make_div(make_p.inner)  </span><br><span class="line">@make_div  </span><br><span class="line">@make_p  </span><br><span class="line">def content():  </span><br><span class="line">    return &#39;人生苦短，我用python&#39;  </span><br><span class="line">  </span><br><span class="line">c &#x3D; content()  </span><br><span class="line">print(c)  </span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">make_p装饰器执行了  </span><br><span class="line">  </span><br><span class="line">make_div装饰器执行了  </span><br><span class="line">  </span><br><span class="line">&lt;div&gt;&lt;p&gt;人生苦短，我用python&lt;&#x2F;p&gt;&lt;&#x2F;div&gt;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="8-带有参数的装饰器"><a href="#8-带有参数的装饰器" class="headerlink" title="8. 带有参数的装饰器"></a>8. 带有参数的装饰器</h3><p>带有参数的装饰器就是使用装饰器装饰函数的时候可以传入指定参数，语法格式: @装饰器(参数,…)。使用带有参数的装饰器，其实是在装饰器外面又包裹了一个函数，使用该函数接收参数，返回是装饰器，因为 @ 符号需要配合装饰器实例使用。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">def return_decorator(flag):  </span><br><span class="line">    # 装饰器只能接收一个参数并且是函数类型  </span><br><span class="line">    def decorator(func):  </span><br><span class="line">        def inner(a, b):  </span><br><span class="line">            if flag &#x3D;&#x3D; &#39;+&#39;:  </span><br><span class="line">                print(&#39;正在努力执行加法计算&#39;)  </span><br><span class="line">            elif flag &#x3D;&#x3D; &#39;-&#39;:  </span><br><span class="line">                print(&#39;正在努力执行减法计算&#39;)  </span><br><span class="line">            func(a, b)  </span><br><span class="line">        return inner  </span><br><span class="line">    # 当调用函数的时候可以返回一个装饰器decorato  </span><br><span class="line">    return decorator  </span><br><span class="line">  </span><br><span class="line">@return_decorator(&#39;+&#39;)  # decorator &#x3D; return_decorator(&#39;+&#39;), @decorator &#x3D;&gt; add_num &#x3D; decorator(add_num)  </span><br><span class="line">def add_num(a, b):  </span><br><span class="line">    result &#x3D; a + b  </span><br><span class="line">    print(result)  </span><br><span class="line">      </span><br><span class="line">@return_decorator(&#39;-&#39;)  </span><br><span class="line">def sub_num(a, b):  </span><br><span class="line">    result &#x3D; a - b  </span><br><span class="line">    print(result)  </span><br><span class="line">  </span><br><span class="line">add_num(1, 2)  </span><br><span class="line">sub_num(1, 2)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="9-类装饰器的使用"><a href="#9-类装饰器的使用" class="headerlink" title="9. 类装饰器的使用"></a>9. 类装饰器的使用</h3><p>类装饰器：使用类装饰已有函数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line">class MyDecorator(object):  </span><br><span class="line">    def __init__(self, func):  </span><br><span class="line">        self.__func &#x3D; func  </span><br><span class="line">  </span><br><span class="line">    # 实现__call__方法，表示对象是一个可调用对象，可以像调用函数一样进行调用  </span><br><span class="line">    def __call__(self, *args, **kwargs):  </span><br><span class="line">        # 对已有函数进行封装  </span><br><span class="line">        print(&#39;马上就有下班啦&#39;)  </span><br><span class="line">        self.__func()  </span><br><span class="line">@MyDecorator  # @MyDecorator &#x3D;&gt; show &#x3D; MyDecorator(show)  </span><br><span class="line">def show():  </span><br><span class="line">    print(&#39;快要下雪啦&#39;)  </span><br><span class="line"># 执行show，就相当于执行MyDecorator类创建的实例对象，show() &#x3D;&gt; 对象()  </span><br><span class="line">show()  </span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">马上就有下班啦  </span><br><span class="line">  </span><br><span class="line">快要下雪啦  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="扩展："><a href="#扩展：" class="headerlink" title="扩展："></a>扩展：</h4><p>函数之所以能够调用，是因为函数内部实现了 <strong>call</strong> 方法。</p>
<h3 id="10-装饰器应用场景"><a href="#10-装饰器应用场景" class="headerlink" title="10. 装饰器应用场景"></a>10. 装饰器应用场景</h3><p>1、收集函数的操作或错误日志记录。</p>
<p>2、验证函数的使用权限。</p>
<p>3、计算函数的运行时间。</p>
<p>4、在ORM/DB模型操作时，通过属性方法动态地获取关联的数据。</p>
<p>5、函数数据的缓存。</p>
<p>6、定制函数的输入和输出（序列化和反序列化）。</p>
<p>_原文来自：<a target="_blank" rel="noopener" href="https://blog.csdn.net/mall/_lucy/article/details/108791699">https://blog.csdn.net/mall\_lucy/article/details/108791699</a><br>作者：南枝向暖北枝寒MA</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/一文看懂Python列表、元组和字符串操作"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82Python%E5%88%97%E8%A1%A8%E3%80%81%E5%85%83%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%93%8D%E4%BD%9C/"
    >一文看懂Python列表、元组和字符串操作.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/%E4%B8%80%E6%96%87%E7%9C%8B%E6%87%82Python%E5%88%97%E8%A1%A8%E3%80%81%E5%85%83%E7%BB%84%E5%92%8C%E5%AD%97%E7%AC%A6%E4%B8%B2%E6%93%8D%E4%BD%9C/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="一文看懂Python列表、元组和字符串操作"><a href="#一文看懂Python列表、元组和字符串操作" class="headerlink" title="一文看懂Python列表、元组和字符串操作"></a>一文看懂Python列表、元组和字符串操作</h1><h1 id="1-列表"><a href="#1-列表" class="headerlink" title="1 列表"></a>1 列表</h1><p>列表（List）是Python中非常重要的内置数据类型。列表由一系列元素组成，所有的元组被包含在一对方括号中。列表被创建将后，可以执行添加、删除、修改操作。</p>
<p>列表中可包含任意的Python数据信息，如字符串、数字、列表、元组等。<br><img src="http://iubest.gitee.io/pic/2020111101.png"></p>
<h2 id="1-1-列表介绍"><a href="#1-1-列表介绍" class="headerlink" title="1.1 列表介绍"></a>1.1 列表介绍</h2><p>列表是有序集合，没有固定大小，能够保存任意数量任意类型的 Python 对象，语法为 <code>[元素1, 元素2, ..., 元素n]</code>。</p>
<p>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; list &#x3D; [ “a”, “b”, “c” ]，定义字符列表。  </span><br><span class="line">&gt;&gt;&gt; list &#x3D; [ 1, 2, 3, 4 ]，定义数字列表。  </span><br><span class="line">&gt;&gt;&gt; list &#x3D; [ [1,2,3,4], [&quot;a&quot;,&quot;b&quot;,&quot;c&quot;] ]，定义列表的列表。  </span><br><span class="line">&gt;&gt;&gt; list &#x3D; [ (1,2,3,4), (&quot;a&quot;,&quot;b&quot;,&quot;c&quot;) ]，定义元组列表。  </span><br><span class="line">&gt;&gt;&gt; list((1,2))把一个元组转换成一个列表[1,2]， list(&#39;test&#39;)可把  </span><br><span class="line">字符串转换成[&#39;t&#39;,&#39;e&#39;,&#39;s&#39;,&#39;t&#39;]列表</span><br></pre></td></tr></table></figure>

<p>列表须知：</p>
<blockquote>
<ul>
<li><p>列表内元素的个数及元素的值可以改变；</p>
</li>
<li><p>列表内元素用中括号（[]）包裹；</p>
</li>
<li><p>列表内不同元素之间采用逗号（,）分隔；</p>
</li>
<li><p>列表内可以包含任何数据类型，也可以包括另一个列表；</p>
</li>
<li><p>列表可以通过序号来访问其中的成员；</p>
</li>
<li><p>可以对列表进行插入、删除、排序，修改列表中某元素等操作。</p>
</li>
</ul>
</blockquote>
<h2 id="1-2-列表操作"><a href="#1-2-列表操作" class="headerlink" title="1.2 列表操作"></a>1.2 列表操作</h2><p>列表的所有操作如下表所示：<br><img src="http://iubest.gitee.io/pic/2020111102.png"></p>
<h3 id="1-2-1-列表脚本操作符"><a href="#1-2-1-列表脚本操作符" class="headerlink" title="1.2.1 列表脚本操作符"></a>1.2.1 列表脚本操作符</h3><p>列表对 + 和 * 的操作符与字符串相似。+ 号用于组合列表，* 号用于重复列表。<img src="http://iubest.gitee.io/pic/2020111103.png"></p>
<h3 id="1-2-2-列表切片"><a href="#1-2-2-列表切片" class="headerlink" title="1.2.2 列表切片"></a>1.2.2 列表切片</h3><p>切片操作（slice）可以从一个列表中获取子列表（列表的一部分）。我们使用一对方括号、起始偏移量start、终止偏移量end 以及可选的步长step 来定义一个分片。<br><img src="http://iubest.gitee.io/pic/2020111104.png"></p>
<blockquote>
<p>格式：[start: end: step]<br>• [:] 提取从开头（默认位置0）到结尾（默认位置-1）的整个字符串<br>• [start:] 从start 提取到结尾<br>• [:end] 从开头提取到end - 1<br>• [start: end] 从start 提取到end - 1<br>• [start: end: step] 从start 提取到end - 1，每step 个字符提取一个<br>• 左侧第一个字符的位置/偏移量为0，右侧最后一个字符的位置/偏移量为-1</p>
</blockquote>
<p>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; list &#x3D; [1, 2, 3, 4, 5, 6, 7]  </span><br><span class="line">&gt;&gt;&gt; list[1:] #列出索引1以后的---------[2, 3, 4, 5, 6, 7]  </span><br><span class="line">&gt;&gt;&gt; list[:-1] #列出索引-1之前的-------[1, 2, 3, 4, 5, 6]  </span><br><span class="line">&gt;&gt;&gt; list[1:3] #列出索引1到3之间的-----[2]  </span><br><span class="line">&gt;&gt;&gt; list[::-1]#[7, 6, 5, 4, 3, 2, 1] #形成reverse函数的效果:</span><br></pre></td></tr></table></figure>

<h3 id="1-2-3-浅拷贝和深拷贝"><a href="#1-2-3-浅拷贝和深拷贝" class="headerlink" title="1.2.3 浅拷贝和深拷贝"></a>1.2.3 浅拷贝和深拷贝</h3><ul>
<li><p>浅拷贝：浅拷贝意味着构造一个新的集合对象，然后用原始对象中找到的子对象的引用来填充它。从本质上讲，浅层的复制只有一层的深度。复制过程不会递归，因此不会创建子对象本身的副本。</p>
</li>
<li><p>深拷贝：深拷贝使复制过程递归。这意味着首先构造一个新的集合对象，然后递归地用在原始对象中找到的子对象的副本填充它。以这种方式复制一个对象，遍历整个对象树，以创建原始对象及其所有子对象的完全独立的克隆。</p>
</li>
</ul>
<p>A. 赋值引用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a &#x3D; &#123;1:[1,2,3]&#125;  </span><br><span class="line">b &#x3D; a  </span><br><span class="line">print(id(a) &#x3D;&#x3D; id(b))</span><br></pre></td></tr></table></figure>

<p>输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">True</span><br></pre></td></tr></table></figure>

<p>赋值引用，a 和 b 都指向同一个对象。<br><img src="http://iubest.gitee.io/pic/2020111105.png"></p>
<p>B. 浅拷贝</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">a &#x3D; &#123;1:[1,2,3]&#125;  </span><br><span class="line">b &#x3D; a.copy()  </span><br><span class="line">print(id(a) &#x3D;&#x3D; id(b))  </span><br><span class="line">print(id(a[1]) &#x3D;&#x3D; id(b[1]))</span><br></pre></td></tr></table></figure>

<p>输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">False  </span><br><span class="line">True</span><br></pre></td></tr></table></figure>

<p>a 和 b 是一个独立的对象，但他们的子对象还是指向统一对象（是引用）。<br><img src="http://iubest.gitee.io/pic/2020111106.png"><br>C. 深度拷贝</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">import copy  </span><br><span class="line">a &#x3D; &#123;1:[1,2,3]&#125;  </span><br><span class="line">b &#x3D; copy.deepcopy(a)  </span><br><span class="line">print(id(a) &#x3D;&#x3D; id(b))  </span><br><span class="line">print(id(a[1]) &#x3D;&#x3D; id(b[1]))  </span><br><span class="line">print(id(a[1][0]) &#x3D;&#x3D; id(b[1][0]))</span><br></pre></td></tr></table></figure>

<p>输出：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">False  </span><br><span class="line">False  </span><br><span class="line">True</span><br></pre></td></tr></table></figure>

<p>a 和 b 完全拷贝了父对象及其子对象，两者是完全独立的。对于a[1][0]和b[1][0]还是对于对象1的引用，并没有重新新建一个对象，这符合python的存储机制。<br><img src="http://iubest.gitee.io/pic/2020111107.png"></p>
<h3 id="1-2-4-其他常用操作"><a href="#1-2-4-其他常用操作" class="headerlink" title="1.2.4 其他常用操作"></a>1.2.4 其他常用操作</h3><p>-—– 列表排序：<br>注意排序优先级：数字&gt;大写字母&gt;小写字母&gt;符号&gt;中文<br>Python list内置sort() 方法用来排序，也可以使用Python内置的全局sorted() 方法对可迭代的序列排序生成新的序列。</p>
<ul>
<li><p>永久性排序：sort()<br><code>list.sort(key=None, reverse=False)</code> 对原列表进行排序。<br><code>key</code> – 主要是用来进行比较的元素，只有一个参数，具体的函数的参数就是取自于可迭代对象中，指定可迭代对象中的一个元素来进行排序。<code>reverse</code> – 排序规则，<code>reverse = True</code> 降序， <code>reverse = False</code> 升序（默认）。该方法没有返回值，但是会对列表的对象进行排序。</p>
</li>
<li><p>临时性排序：sorted()<br>Python内置函数，</p>
</li>
<li><p>反转排序：reverse()</p>
</li>
</ul>
<p>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">&#96;&gt;&gt;&gt; list1 &#x3D; [3, 5, 1, 6, 9]  </span><br><span class="line">&gt;&gt;&gt; lsit2 &#x3D; [3, 5, 1, 6, 9]  </span><br><span class="line">&gt;&gt;&gt; a &#x3D; list1.sort()   #永久性排序，就是这个列表就变了  </span><br><span class="line">&gt;&gt;&gt; print(a, l1)   </span><br><span class="line"># None [1, 3, 5, 6, 9]  </span><br><span class="line">      </span><br><span class="line">&gt;&gt;&gt; b &#x3D;sorted(list1)    #临时性排序，就是可以赋值某个变量  </span><br><span class="line">&gt;&gt;&gt; print(b, list1)  </span><br><span class="line"># [1, 3, 5, 6, 9] [3, 5, 1, 6, 9]  </span><br><span class="line">  </span><br><span class="line">&gt;&gt;&gt; c &#x3D; list2.reverse()  </span><br><span class="line">&gt;&gt;&gt; print(c, list2)  </span><br><span class="line"># None [9, 6, 1, 5, 3]&#96;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>-—– list中的append和extend的区别：</p>
<ul>
<li><p>list.append(object) 向列表中添加一个对象object</p>
</li>
<li><p>list.extend(sequence) 把一个序列seq的内容添加到列表中</p>
</li>
</ul>
<h1 id="2-元组"><a href="#2-元组" class="headerlink" title="2 元组"></a>2 元组</h1><h2 id="2-1-元组操作"><a href="#2-1-元组操作" class="headerlink" title="2.1 元组操作"></a>2.1 元组操作</h2><p>Python 的元组与列表类似，不同之处在于tuple被创建后就不能对其进行修改，类似字符串。</p>
<ul>
<li><p>元组使用小括号，列表使用方括号。</p>
</li>
<li><p>元组可以使用在不希望数据被其他操作改变的场合。<br><img src="http://iubest.gitee.io/pic/2020111108.png"></p>
</li>
</ul>
<h2 id="2-2-解压元组"><a href="#2-2-解压元组" class="headerlink" title="2.2 解压元组"></a>2.2 解压元组</h2><p>解压（unpack）一维元组（有几个元素左边括号定义几个变量）<br>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">(a, b, c)  &#x3D; (1, 10.31, &#39;python&#39;)  </span><br><span class="line">print(a, b, c)  </span><br><span class="line"># 1 10.31 python</span><br></pre></td></tr></table></figure>

<p>解压二维元组（按照元组里的元组结构来定义变量）<br>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">t &#x3D; (1, 10.31, (&#39;OK&#39;, &#39;python&#39;))  </span><br><span class="line">(a, b, (c, d)) &#x3D; t  </span><br><span class="line">print(a, b, c, d)  </span><br><span class="line"># 1 10.31 OK python</span><br></pre></td></tr></table></figure>

<p>如果你只想要元组其中几个元素，用通配符「*」，英文叫 wildcard，在计算机语言中代表一个或多个元素。下例就是把多个元素丢给了 <code>rest</code> 变量。<br>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">t &#x3D; 1, 2, 3, 4, 5  </span><br><span class="line">a, b, *rest, c &#x3D; t  </span><br><span class="line">print(a, b, c)  # 1 2 5  </span><br><span class="line">print(rest)  # [3, 4]</span><br></pre></td></tr></table></figure>

<p>如果你根本不在乎 rest 变量，那么就用通配符「*」加上下划线「_」。<br>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a, b, *_ &#x3D; t  </span><br><span class="line">print(a, b)  # 1 2</span><br></pre></td></tr></table></figure>

<h1 id="3-字符串"><a href="#3-字符串" class="headerlink" title="3 字符串"></a>3 字符串</h1><h2 id="3-1-字符串介绍"><a href="#3-1-字符串介绍" class="headerlink" title="3.1 字符串介绍"></a>3.1 字符串介绍</h2><p>字符串的定义：<br>字符串为引号之间的字符集合，这里引号包括单引号、双引号，三引号（三个连续的单引号或双引号）。</p>
<p>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; s1&#x3D;&#39;I love Python&#39;  </span><br><span class="line">&gt;&gt;&gt; s1  </span><br><span class="line">&#39;I love Python&#39;  </span><br><span class="line">  </span><br><span class="line">&gt;&gt;&gt; s2&#x3D;str([1,2,3])  </span><br><span class="line">&gt;&gt;&gt; s2  </span><br><span class="line">&#39;[1, 2, 3]&#39; </span><br></pre></td></tr></table></figure>

<p>Python的转义字符  </p>
<p>字符串前加 u、r、b</p>
<ul>
<li><p>u”中文字符组成的字符串”<br>作用：以 Unicode 格式 进行编码，一般用在中文字符串前面，防止因为源码储存格式问题，导致再次使用时出现乱码。</p>
</li>
<li><p>r”\n\n\n\n”　　# 表示一个普通生字符串 \n\n\n\n，而不表示换行<br>作用：去掉反斜杠的转义机制，常用于正则表达式，对应着re模块。</p>
</li>
<li><p>b’Hello World’   # 表示这是一个 bytes 对象<br>作用：b” “前缀表示：后面字符串是bytes 类型。在网络编程中，服务器和浏览器只认bytes 类型数据。在 Python3 中，bytes 和 str 的互相转换方式是str.encode(‘utf-8’)和bytes.decode(‘utf-8’)。</p>
</li>
</ul>
<h2 id="3-2-字符串格式化"><a href="#3-2-字符串格式化" class="headerlink" title="3.2 字符串格式化"></a>3.2 字符串格式化</h2><p>Python 支持格式化字符串的输出 。尽管这样可能会用到非常复杂的表达式，但最基本的用法是将一个值插入到一个有字符串格式符 %s 的字符串中。在 Python 中，字符串格式化使用与 C 中 sprintf 函数一样的语法。<br>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#96;print &quot;My name is %s and weight is %d kg!&quot; % (&#39;Zara&#39;, 21)  </span><br><span class="line"># My name is Zara and weight is 21 kg!&#96;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>python 字符串格式化符号:<br><img src="http://iubest.gitee.io/pic/2020111110.png"><br>格式化操作符辅助指令：<br><img src="http://iubest.gitee.io/pic/2020111111.png"></p>
<h2 id="3-3-format-格式化函数"><a href="#3-3-format-格式化函数" class="headerlink" title="3.3 format 格式化函数"></a>3.3 format 格式化函数</h2><p>Python2.6 开始，新增了一种格式化字符串的函数 str.format()，它增强了字符串格式化的功能。</p>
<ul>
<li><p>基本语法是通过 {} 和 : 来代替以前的 % 。</p>
</li>
<li><p>format 函数可以接受不限个参数，位置可以不按顺序。</p>
</li>
</ul>
<p>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt;&quot;&#123;&#125; &#123;&#125;&quot;.format(&quot;hello&quot;, &quot;world&quot;)    # 不设置指定位置，按默认顺序  </span><br><span class="line">&#39;hello world&#39;  </span><br><span class="line">   </span><br><span class="line">&gt;&gt;&gt; &quot;&#123;0&#125; &#123;1&#125;&quot;.format(&quot;hello&quot;, &quot;world&quot;)  # 设置指定位置  </span><br><span class="line">&#39;hello world&#39;  </span><br><span class="line">   </span><br><span class="line">&gt;&gt;&gt; &quot;&#123;1&#125; &#123;0&#125; &#123;1&#125;&quot;.format(&quot;hello&quot;, &quot;world&quot;)  # 设置指定位置  </span><br><span class="line">&#39;world hello world&#39;</span><br></pre></td></tr></table></figure>

<ul>
<li>也可以设置参数：</li>
</ul>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">print(&quot;网站名：&#123;name&#125;, 地址 &#123;url&#125;&quot;.format())  </span><br><span class="line">   </span><br><span class="line"># 通过字典设置参数  </span><br><span class="line">site &#x3D; &#123;&quot;name&quot;: &quot;菜鸟教程&quot;, &quot;url&quot;: &quot;www.runoob.com&quot;&#125;  </span><br><span class="line">print(&quot;网站名：&#123;name&#125;, 地址 &#123;url&#125;&quot;.format(**site))  </span><br><span class="line">   </span><br><span class="line"># 通过列表索引设置参数  </span><br><span class="line">my_list &#x3D; [&#39;菜鸟教程&#39;, &#39;www.runoob.com&#39;]  </span><br><span class="line">print(&quot;网站名：&#123;0[0]&#125;, 地址 &#123;0[1]&#125;&quot;.format(my_list))  # &quot;0&quot; 是必须的</span><br></pre></td></tr></table></figure>

<p>数字格式化<br><img src="http://iubest.gitee.io/pic/2020111112.png"></p>
<blockquote>
<p>^, &lt;, &gt; 分别是居中、左对齐、右对齐，后面带宽度， : 号后面带填充的字符，只能是一个字符，不指定则默认是用空格填充。<br>+ 表示在正数前显示 +，负数前显示 -；（空格）表示在正数前加空格<br>b、d、o、x 分别是二进制、十进制、八进制、十六进制。</p>
</blockquote>
<p>【例子】</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; print(&quot;&#123;:.2f&#125;&quot;.format(3.1415926));  </span><br><span class="line">3.14  </span><br><span class="line">  </span><br><span class="line">&gt;&gt;&gt; print (&quot;&#123;&#125; 对应的位置是 &#123;&#123;0&#125;&#125;&quot;.format(&quot;runoob&quot;))  </span><br><span class="line">runoob 对应的位置是 &#123;0&#125;</span><br></pre></td></tr></table></figure>

<p>练习题：</p>
<p>1、<strong>列表操作练习</strong><br>列表lst 内容如下<br>lst = [2, 5, 6, 7, 8, 9, 2, 9, 9]<br>请写程序完成下列操作：</p>
<ol>
<li><p>在列表的末尾增加元素15</p>
</li>
<li><p>在列表的中间位置插入元素20</p>
</li>
<li><p>将列表[2, 5, 6]合并到lst中</p>
</li>
<li><p>移除列表中索引为3的元素</p>
</li>
<li><p>翻转列表里的所有元素</p>
</li>
<li><p>对列表里的元素进行排序，从小到大一次，从大到小一次</p>
</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; lst &#x3D; [2, 5, 6, 7, 8, 9, 2, 9, 9]  </span><br><span class="line">&gt;&gt;&gt; lst.append(15)  </span><br><span class="line">&gt;&gt;&gt; lst.insert(5,20)  </span><br><span class="line">&gt;&gt;&gt; lst.extend([2, 5, 6])  </span><br><span class="line">&gt;&gt;&gt; del lst[3]  </span><br><span class="line">&gt;&gt;&gt; lst.reverse()  </span><br><span class="line">&gt;&gt;&gt; lst.sort()  </span><br><span class="line">&gt;&gt;&gt; lst.sort(reverse&#x3D;True)</span><br></pre></td></tr></table></figure>

<p><strong>原文地址</strong></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/OuDiShenmiss/article/details/107599379">https://blog.csdn.net/OuDiShenmiss/article/details/107599379</a></p>
<p><strong>相关阅读</strong>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247483689&idx=1&sn=3c6e345f0dc083450a034a292abcdcba&chksm=a73c6111904be8070fda0c5e64f9263193936aa9e80da13f0f8d77ad6559b431b4d576c0095c&scene=21#wechat_redirect">一文看懂Python面向对象编程(Python学习与新手入门必看)-绝对原创</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247484084&idx=1&sn=573989b9526aef01a3d515ab09afe86a&chksm=a73c628c904beb9a39adef9b95a1ce6560245b7f4e2a39207a55abc1a293935be203a35bcb13&scene=21#wechat_redirect">一文看懂Python多进程与多线程编程(工作学习面试必读)</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247484141&idx=1&sn=4a640751190159558bdb7db6d5b3d529&chksm=a73c62d5904bebc3a780224a2f6f1033a6ef8c1568f3a36585961d4d1fc3d3a942d31aa2e7fe&scene=21#wechat_redirect">一文看懂Python字典类型数据常见操作及排序</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247484195&idx=1&sn=0f92015bf28c53ad4ae48a5cc1d9e0da&chksm=a73c631b904bea0d17e994b76ddbb78b18a3bdada1162dfdec4f023efe3bd8bca4dfda13018f&scene=21#wechat_redirect">一文看懂Python系列之装饰器(decorator)(工作面试必读)</a>  </p>
<p><a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MjM5OTMyODA4Nw==&mid=2247484163&idx=1&sn=a84fde69f4fb4d3ecab35856f8b71ca6&chksm=a73c633b904bea2d91564945bf51caa8131d8d65505d759ae0aa3a06074c6e7890e597eb48ff&scene=21#wechat_redirect">一文看懂Python的re模块及正则表达式(regex)</a>  </p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/使用 IntelliJ IDEA 查看类图，内容极度舒适"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/%E4%BD%BF%E7%94%A8%20IntelliJ%20IDEA%20%E6%9F%A5%E7%9C%8B%E7%B1%BB%E5%9B%BE%EF%BC%8C%E5%86%85%E5%AE%B9%E6%9E%81%E5%BA%A6%E8%88%92%E9%80%82/"
    >使用 IntelliJ IDEA 查看类图，内容极度舒适.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/%E4%BD%BF%E7%94%A8%20IntelliJ%20IDEA%20%E6%9F%A5%E7%9C%8B%E7%B1%BB%E5%9B%BE%EF%BC%8C%E5%86%85%E5%AE%B9%E6%9E%81%E5%BA%A6%E8%88%92%E9%80%82/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="使用-IntelliJ-IDEA-查看类图，内容极度舒适"><a href="#使用-IntelliJ-IDEA-查看类图，内容极度舒适" class="headerlink" title="使用 IntelliJ IDEA 查看类图，内容极度舒适"></a>使用 IntelliJ IDEA 查看类图，内容极度舒适</h1><p>最近正好也没什么可忙的，就回过头来鼓捣过去的知识点，到Servlet部分时，以前学习的时候硬是把从上到下的继承关系和接口实现记得乱七八糟。</p>
<p>这次利用了IDEA的diagram，结果一目了然，也是好用到炸裂，就此分享。</p>
<p>查看图形形式的继承链</p>
<p>在你想查看的类的标签页内，点击右键，选择 Diagrams，其中有 show 和 show … Popup，只是前者新建在标签页内，后者以浮窗的形式展示：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018065.png" alt="img"></p>
<p>实际上，你也可以从左边的项目目录树中，对你想查看的类点击右键，同样选择Diagrams，效果是一样的：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018084.png" alt="img"></p>
<p>然后你就会得到如下图所示的继承关系图形，以自定义的Servlet为例：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018211.png" alt="img"></p>
<p>显而易见的是：</p>
<ul>
<li><strong>蓝色实线箭头</strong>是指继承关系</li>
<li><strong>绿色虚线箭头</strong>是指接口实现关系</li>
</ul>
<p>优化继承链图形，想我所想</p>
<h2 id="1-去掉不关心的类"><a href="#1-去掉不关心的类" class="headerlink" title="1 去掉不关心的类"></a>1 去掉不关心的类</h2><p>得到的继承关系图形，有些并不是我们想去了解的，比如上图的Object和Serializable，我们只想关心Servlet重要的那几个继承关系，怎么办？</p>
<p>简单，删掉。点击选择你想要删除的类，然后直接使用键盘上的delete键就行了。清理其他类的关系后图形如下：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018210.png" alt="img"></p>
<h2 id="2-展示类的详细信息"><a href="#2-展示类的详细信息" class="headerlink" title="2 展示类的详细信息"></a>2 展示类的详细信息</h2><p>有人说，诶，这怎么够呢，那继承下来的那些方法我也想看啊？简单，IDEA通通满足你。</p>
<p>在页面点击右键，选择 show categories，根据需要可以展开类中的属性、方法、构造方法等等。当然，第二种方法也可以直接使用上面的工具栏：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018210.png" alt="img"></p>
<p>然后你就会得到：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018210.png" alt="img"></p>
<p>什么，方法里你还想筛选，比如说想看protected权限及以上范围的？简单，右键选择 Change Visibility Level，根据需要调整即可。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017036014.png" alt="img"></p>
<p>什么，你嫌图形太小你看不清楚？IDEA也可以满足你，按住键盘的Alt，竟然出现了放大镜，惊不惊喜，意不意外？</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018212.png" alt="img"></p>
<h2 id="3-加入其他类到关系中来"><a href="#3-加入其他类到关系中来" class="headerlink" title="3 加入其他类到关系中来"></a>3 加入其他类到关系中来</h2><p>当我们还需要查看其他类和当前类是否有继承上的关系的时候，我们可以选择加其加入到当前的继承关系图形中来。</p>
<p>在页面点击右键，选择 Add Class to Diagram，然后输入你想加入的类就可以了：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018211.png" alt="img"></p>
<p>例如我们添加了一个Student类，如下图所示。好吧，并没有任何箭头，看来它和当前这几个类以及接口并没有发生什么不可描述的关系：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018211.png" alt="img"></p>
<h2 id="4-查看具体代码"><a href="#4-查看具体代码" class="headerlink" title="4 查看具体代码"></a>4 查看具体代码</h2><p>如果你想查看某个类中，比如某个方法的具体源码，当然，不可能给你展现在图形上了，不然屏幕还不得撑炸？</p>
<p>但是可以利用图形，或者配合IDEA的structure方便快捷地进入某个类的源码进行查看。</p>
<p>双击某个类后，你就可以在其下的方法列表中游走，对于你想查看的方法，选中后点击右键，选择 Jump to Source：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018212.webp" alt="img"></p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018212.webp" alt="img"></p>
<p>在进入某个类后，如果还想快速地查看该类的其他方法，还可以利用IDEA提供的structure功能：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601017018213.webp" alt="img"></p>
<p>选择左侧栏的structure之后，如上图左侧会展示该类中的所有方法，点击哪个方法，页面内容就会跳转到该方法部分去。</p>
<p>最后</p>
<p>用上面提到的的IDEA这些功能，学习和查看类关系，了解诸如主流框架源码之类的东西，可以说是非常舒服了。</p>
<p>来源：<a target="_blank" rel="noopener" href="http://www.cnblogs.com/deng-cc/p/6927447.html">www.cnblogs.com/deng-cc/p/6927447.html</a></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/使用Django开发CMDB资产管理平台"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/%E4%BD%BF%E7%94%A8Django%E5%BC%80%E5%8F%91CMDB%E8%B5%84%E4%BA%A7%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0/"
    >使用Django开发CMDB资产管理平台.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/%E4%BD%BF%E7%94%A8Django%E5%BC%80%E5%8F%91CMDB%E8%B5%84%E4%BA%A7%E7%AE%A1%E7%90%86%E5%B9%B3%E5%8F%B0/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="使用Django开发CMDB资产管理平台"><a href="#使用Django开发CMDB资产管理平台" class="headerlink" title="使用Django开发CMDB资产管理平台"></a>使用Django开发CMDB资产管理平台</h1><h2 id="快速搭建Django项目"><a href="#快速搭建Django项目" class="headerlink" title="快速搭建Django项目"></a>快速搭建Django项目</h2><p>注意<a target="_blank" rel="noopener" href="https://docs.djangoproject.com/en/3.1/faq/install/">版本的选择</a>，本项目选择Django 2.2.8，高手推荐3.0+。</p>
<table>
<thead>
<tr>
<th>Django version</th>
<th>Python versions</th>
</tr>
</thead>
<tbody><tr>
<td>1.11</td>
<td>2.7, 3.4, 3.5, 3.6, 3.7 (added in 1.11.17)</td>
</tr>
<tr>
<td>2.0</td>
<td>3.4, 3.5, 3.6, 3.7</td>
</tr>
<tr>
<td>2.1</td>
<td>3.5, 3.6, 3.7</td>
</tr>
<tr>
<td>2.2</td>
<td>3.5, 3.6, 3.7, 3.8 (added in 2.2.8), 3.9 (added in 2.2.17)</td>
</tr>
<tr>
<td>3.0</td>
<td>3.6, 3.7, 3.8, 3.9 (added in 3.0.11)</td>
</tr>
<tr>
<td>3.1</td>
<td>3.6, 3.7, 3.8, 3.9 (added in 3.1.3)</td>
</tr>
</tbody></table>
<h2 id="使用虚拟环境"><a href="#使用虚拟环境" class="headerlink" title="使用虚拟环境"></a>使用虚拟环境</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">#安装</span><br><span class="line">pip install virtualenv</span><br><span class="line">#创建虚拟目录</span><br><span class="line">virtualenv venv</span><br><span class="line">#激活虚拟环境</span><br><span class="line">source venv&#x2F;bin&#x2F;activate</span><br></pre></td></tr></table></figure>

<h3 id="安装Django"><a href="#安装Django" class="headerlink" title="安装Django"></a>安装Django</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">pip install Django&#x3D;&#x3D;2.2.8</span><br><span class="line">#查看当前版本</span><br><span class="line">pip list|grep Django</span><br></pre></td></tr></table></figure>

<h3 id="创建项目"><a href="#创建项目" class="headerlink" title="创建项目"></a>创建项目</h3><p>使用 django-admin 来创建 CMDBWEB 项目：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">django-admin startproject CMDBWEB</span><br></pre></td></tr></table></figure>
<p>目录说明：</p>
<ul>
<li><strong>CMDBWEB:</strong> 项目的容器。</li>
<li><strong>manage.py:</strong> 一个实用的命令行工具，可让你以各种方式与该 Django 项目进行交互。</li>
<li><strong>CMDBWEB/<strong>init</strong>.py:</strong> 一个空文件，告诉 Python 该目录是一个 Python 包。</li>
<li><strong>CMDBWEB/settings.py:</strong> 该 Django 项目的设置/配置。</li>
<li><strong>CMDBWEB/urls.py:</strong> 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站”目录”。</li>
<li><strong>CMDBWEB/wsgi.py:</strong> 一个 WSGI 兼容的 Web 服务器的入口，以便运行你的项目。<br>接下来我们进入 CMDBWEB 目录输入以下命令，启动服务器：<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python3 manage.py runserver</span><br></pre></td></tr></table></figure>
浏览器访问 <a href="http://127.0.0.1:8000，看到小火箭，就成功了一小步。">http://127.0.0.1:8000，看到小火箭，就成功了一小步。</a></li>
</ul>
<p>创建管理员账号(可选)</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">python manage.py createsuperuser</span><br><span class="line">#浏览器访问 http:&#x2F;&#x2F;127.0.0.1:8000&#x2F;admin</span><br></pre></td></tr></table></figure>

<p>使用 django-admin 来创建 CMDBWEB APP(模块)：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">django-admin startapp ServerMgr</span><br></pre></td></tr></table></figure>

<p>接下来在settings.py中找到INSTALLED_APPS这一项，如下：</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">INSTALLED_APPS = (</span><br><span class="line">    &#x27;django.contrib.admin&#x27;,</span><br><span class="line">    &#x27;django.contrib.auth&#x27;,</span><br><span class="line">    &#x27;django.contrib.contenttypes&#x27;,</span><br><span class="line">    &#x27;django.contrib.sessions&#x27;,</span><br><span class="line">    &#x27;django.contrib.messages&#x27;,</span><br><span class="line">    &#x27;django.contrib.staticfiles&#x27;,</span><br><span class="line">    &#x27;ServerMgr&#x27;,               # 添加此项</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>模板路径必须改：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">TEMPLATES &#x3D; [</span><br><span class="line">    &#123;</span><br><span class="line">        &#39;BACKEND&#39;: &#39;django.template.backends.django.DjangoTemplates&#39;,</span><br><span class="line">        &#39;DIRS&#39;: [os.path.join(BASE_DIR, &#39;templates&#39;)], #修改此项</span><br><span class="line">        &#39;APP_DIRS&#39;: True,</span><br><span class="line">        &#39;OPTIONS&#39;: &#123;</span><br><span class="line">            &#39;context_processors&#39;: [</span><br><span class="line">                &#39;django.template.context_processors.debug&#39;,</span><br><span class="line">                &#39;django.template.context_processors.request&#39;,</span><br><span class="line">                &#39;django.contrib.auth.context_processors.auth&#39;,</span><br><span class="line">                &#39;django.contrib.messages.context_processors.messages&#39;,</span><br><span class="line">            ],</span><br><span class="line">        &#125;,</span><br><span class="line">    &#125;,</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>修改其他配置（可选）</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">注释以下行</span><br><span class="line"># &#39;django.middleware.csrf.CsrfViewMiddleware&#39;,</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">#修改时区</span><br><span class="line">LANGUAGE_CODE &#x3D; &#39;zh-hans&#39;</span><br><span class="line">TIME_ZONE &#x3D; &#39;Asia&#x2F;Shanghai&#39;</span><br><span class="line">USE_I18N &#x3D; True</span><br><span class="line">USE_L10N &#x3D; True</span><br><span class="line">USE_TZ &#x3D; True</span><br></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#允许所有IP</span><br><span class="line">ALLOWED_HOSTS &#x3D; [&#39;*&#39;]</span><br></pre></td></tr></table></figure>

<p>在主目录的urls中添加访问路径</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path</span><br><span class="line"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> url</span><br><span class="line"><span class="keyword">from</span> ServerMgr <span class="keyword">import</span> views <span class="keyword">as</span> servermgr</span><br><span class="line">urlpatterns = [</span><br><span class="line">    path(<span class="string">&#x27;admin/&#x27;</span>, admin.site.urls),</span><br><span class="line">    path(<span class="string">&#x27;index&#x27;</span>, servermgr.index),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>视图和 URL 配置</p>
<p>在根目录下创建templates目录，在templates目录下新建index.html文件，代码如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&lt;h1&gt;&#123;&#123; hello &#125;&#125;&lt;&#x2F;h1&gt;</span><br></pre></td></tr></table></figure>
<p>修改ServerMgr/views.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">index</span>(<span class="params">request</span>):</span></span><br><span class="line">    context= &#123;&#125;</span><br><span class="line">    context[<span class="string">&#x27;hello&#x27;</span>] = <span class="string">&#x27;Hello World!&#x27;</span></span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;index.html&#x27;</span>, context)</span><br></pre></td></tr></table></figure>

<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">#启动项目</span><br><span class="line">python3 manage.py runserver</span><br></pre></td></tr></table></figure>

<p>浏览器访问：<a target="_blank" rel="noopener" href="http://127.0.0.1:8000/index">http://127.0.0.1:8000/index</a></p>
<h2 id="配置数据库"><a href="#配置数据库" class="headerlink" title="配置数据库"></a>配置数据库</h2><p>这里使用sqlite进行操作，在settings.py中进行配置：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">DATABASES = &#123;</span><br><span class="line">    <span class="string">&#x27;default&#x27;</span>: &#123;</span><br><span class="line">        <span class="string">&#x27;ENGINE&#x27;</span>: <span class="string">&#x27;django.db.backends.sqlite3&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;NAME&#x27;</span>: os.path.join(BASE_DIR, <span class="string">&#x27;db.sqlite3&#x27;</span>),</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Mysql配置参考：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">DATABASES = &#123;</span><br><span class="line">    <span class="string">&#x27;default&#x27;</span>: &#123;</span><br><span class="line">        <span class="string">&#x27;ENGINE&#x27;</span>: <span class="string">&#x27;django.db.backends.mysql&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;NAME&#x27;</span>: <span class="string">&#x27;djangoblog&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;USER&#x27;</span>: <span class="string">&#x27;root&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;PASSWORD&#x27;</span>: <span class="string">&#x27;password&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;HOST&#x27;</span>: <span class="string">&#x27;host IP address&#x27;</span>,</span><br><span class="line">        <span class="string">&#x27;PORT&#x27;</span>: <span class="number">3306</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>数据库链接工具推荐：Navicat 或DBeaver（支持sqlite）</p>
<h2 id="Django自动建表（推荐）"><a href="#Django自动建表（推荐）" class="headerlink" title="Django自动建表（推荐）"></a>Django自动建表（推荐）</h2><p>（自动创建自增id）</p>
<p>修改ServerMgr/models.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ServerInfo</span>(<span class="params">models.Model</span>):</span></span><br><span class="line">    hostname = models.TextField(max_length=<span class="number">50</span>)</span><br><span class="line">    os = models.TextField(max_length=<span class="number">50</span>)</span><br><span class="line">    cpu = models.TextField(max_length=<span class="number">10</span>)</span><br><span class="line">    memory = models.TextField(max_length=<span class="number">10</span>)</span><br><span class="line">    ip = models.TextField(max_length=<span class="number">20</span>)</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">Meta</span>:</span></span><br><span class="line">        db_table = <span class="string">&#x27;SERVER_INFO&#x27;</span></span><br></pre></td></tr></table></figure>

<h3 id="自动建表"><a href="#自动建表" class="headerlink" title="自动建表"></a>自动建表</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">python manage.py makemigrations</span><br><span class="line">python manage.py migrate</span><br></pre></td></tr></table></figure>

<h2 id="使用SQL建表（可选）"><a href="#使用SQL建表（可选）" class="headerlink" title="使用SQL建表（可选）"></a>使用SQL建表（可选）</h2><h3 id="建表"><a href="#建表" class="headerlink" title="建表"></a>建表</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> SERVER_INFO (</span><br><span class="line">	<span class="keyword">id</span> <span class="built_in">INTEGER</span> <span class="keyword">NOT</span> <span class="literal">NULL</span> PRIMARY <span class="keyword">KEY</span> AUTOINCREMENT,</span><br><span class="line">	hostname <span class="built_in">TEXT</span>(<span class="number">50</span>),</span><br><span class="line">	os <span class="built_in">TEXT</span>(<span class="number">50</span>),</span><br><span class="line">	cpu <span class="built_in">TEXT</span>(<span class="number">10</span>),</span><br><span class="line">	<span class="keyword">memory</span> <span class="built_in">TEXT</span>(<span class="number">10</span>),</span><br><span class="line">	ip <span class="built_in">TEXT</span>(<span class="number">20</span>)</span><br><span class="line">);</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="反向生成模型"><a href="#反向生成模型" class="headerlink" title="反向生成模型"></a>反向生成模型</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage.py inspectdb SERVER_INFO &gt;&gt;ServerMgr&#x2F;models.py</span><br></pre></td></tr></table></figure>

<h2 id="插入测试数据"><a href="#插入测试数据" class="headerlink" title="插入测试数据"></a>插入测试数据</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> SERVER_INFO (hostname,os,cpu,<span class="keyword">memory</span>,ip) <span class="keyword">VALUES</span> (<span class="string">&#x27;Ali-server1&#x27;</span>,<span class="string">&#x27;Centos7&#x27;</span>,<span class="string">&#x27;2C&#x27;</span>,<span class="string">&#x27;2G&#x27;</span>,<span class="string">&#x27;47.103.211.23&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> SERVER_INFO (hostname,os,cpu,<span class="keyword">memory</span>,ip) <span class="keyword">VALUES</span> (<span class="string">&#x27;Tx-server1&#x27;</span>,<span class="string">&#x27;Ubuntu20&#x27;</span>,<span class="string">&#x27;2C&#x27;</span>,<span class="string">&#x27;2G&#x27;</span>,<span class="string">&#x27;49.234.187.88&#x27;</span>);</span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> SERVER_INFO (hostname,os,cpu,<span class="keyword">memory</span>,ip) <span class="keyword">VALUES</span> (<span class="string">&#x27;Hw-server1&#x27;</span>,<span class="string">&#x27;Centos7&#x27;</span>,<span class="string">&#x27;2C&#x27;</span>,<span class="string">&#x27;4G&#x27;</span>,<span class="string">&#x27;121.37.171.107&#x27;</span>);</span><br></pre></td></tr></table></figure>

<h2 id="数据库操作"><a href="#数据库操作" class="headerlink" title="数据库操作"></a>数据库操作</h2><h3 id="数据查询"><a href="#数据查询" class="headerlink" title="数据查询"></a>数据查询</h3><p>urls.py添加代码，注意/不要少</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> ServerMgr <span class="keyword">import</span> views <span class="keyword">as</span> servermgr</span><br><span class="line">urlpatterns = [</span><br><span class="line">    path(<span class="string">&#x27;admin/&#x27;</span>, admin.site.urls),</span><br><span class="line">    path(<span class="string">&#x27;index/&#x27;</span>, servermgr.index),</span><br><span class="line">    path(<span class="string">&#x27;show/&#x27;</span>, servermgr.show),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>修改ServerMgr/views.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> ServerInfo</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">show</span>(<span class="params">request</span>):</span></span><br><span class="line">    res_list = ServerInfo.objects.values(<span class="string">&#x27;hostname&#x27;</span>,<span class="string">&#x27;os&#x27;</span>,<span class="string">&#x27;cpu&#x27;</span>,<span class="string">&#x27;memory&#x27;</span>,<span class="string">&#x27;ip&#x27;</span>)</span><br><span class="line">    context = &#123;<span class="string">&#x27;title&#x27;</span>: <span class="string">&#x27;首页&#x27;</span>, <span class="string">&#x27;res_list&#x27;</span>: res_list&#125;</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">&#x27;serverlist.html&#x27;</span>, context=context)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以使用locals()</span></span><br><span class="line"><span class="comment"># def show(request):</span></span><br><span class="line"><span class="comment">#     res_list = ServerInfo.objects.values(&#x27;hostname&#x27;,&#x27;os&#x27;,&#x27;cpu&#x27;,&#x27;memory&#x27;,&#x27;ip&#x27;)</span></span><br><span class="line"><span class="comment">#     return render(request, &#x27;serverlist.html&#x27;, context=locals())</span></span><br></pre></td></tr></table></figure>

<p>添加templates/serverlist.html</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;!DOCTYPE <span class="meta-keyword">html</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">html</span> <span class="attr">lang</span>=<span class="string">&quot;en&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">head</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">&quot;utf-8&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">title</span>&gt;</span><span class="tag">&lt;/<span class="name">title</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">head</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;<span class="name">table</span> <span class="attr">class</span>=<span class="string">&quot;table mb0 pr15 ranger-box2  &quot;</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">thead</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;<span class="name">tr</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">th</span> <span class="attr">style</span>=<span class="string">&quot;width: 15%;&quot;</span>&gt;</span>主机名<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">th</span> <span class="attr">style</span>=<span class="string">&quot;width: 15%;&quot;</span>&gt;</span>类别<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">th</span> <span class="attr">style</span>=<span class="string">&quot;width: 15%;&quot;</span>&gt;</span>CPU<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">th</span> <span class="attr">style</span>=<span class="string">&quot;width: 15%;&quot;</span>&gt;</span>内存<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">th</span> <span class="attr">style</span>=<span class="string">&quot;width: 15%;&quot;</span>&gt;</span>IP<span class="tag">&lt;/<span class="name">th</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;/<span class="name">thead</span>&gt;</span></span><br><span class="line">                        <span class="tag">&lt;<span class="name">tbody</span>&gt;</span></span><br><span class="line">                        &#123;% for res in res_list %&#125;</span><br><span class="line">                            <span class="tag">&lt;<span class="name">tr</span>&gt;</span></span><br><span class="line">                                <span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123; res.hostname &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span><span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123; res.os &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span><span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123; res.cpu &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span><span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123; res.memory &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span><span class="tag">&lt;<span class="name">td</span>&gt;</span>&#123;&#123; res.ip &#125;&#125;<span class="tag">&lt;/<span class="name">td</span>&gt;</span></span><br><span class="line">                            <span class="tag">&lt;/<span class="name">tr</span>&gt;</span></span><br><span class="line">                        &#123;% endfor %&#125;</span><br><span class="line">                        <span class="tag">&lt;/<span class="name">tbody</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">table</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">html</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>浏览器访问：<a target="_blank" rel="noopener" href="http://127.0.0.1:8000/show">http://127.0.0.1:8000/show</a></p>
<h3 id="添加数据"><a href="#添加数据" class="headerlink" title="添加数据"></a>添加数据</h3><p>修改urls.py</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">from ServerMgr import views as servermgr</span><br><span class="line"></span><br><span class="line">urlpatterns &#x3D; [</span><br><span class="line">    path(&#39;admin&#x2F;&#39;, admin.site.urls),</span><br><span class="line">    path(&#39;show&#x2F;&#39;, servermgr.show),</span><br><span class="line">    path(&#39;addserver&#x2F;&#39;, servermgr.addserver),</span><br><span class="line"></span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>添加数据需要先创建对象，然后再执行 save 函数，相当于SQL中的INSERT</p>
<p>在ServerMgr中修改views.py, 代码如下：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponse</span><br><span class="line"><span class="keyword">from</span> .models <span class="keyword">import</span> ServerInfo</span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">addserver</span>(<span class="params">request</span>):</span></span><br><span class="line">    server1 = ServerInfo(hostname=<span class="string">&#x27;hw-server2&#x27;</span>,os=<span class="string">&#x27;Centos7&#x27;</span>,cpu=<span class="string">&#x27;4c&#x27;</span>,memory=<span class="string">&#x27;8G&#x27;</span>,ip=<span class="string">&#x27;192.168.1.111&#x27;</span>)</span><br><span class="line">    server1.save()</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(<span class="string">&quot;&lt;p&gt;数据添加成功！&lt;/p&gt;&quot;</span>)</span><br></pre></td></tr></table></figure>

<p>新建views.py, 配置如下：浏览器访问</p>
<p><a target="_blank" rel="noopener" href="http://127.0.0.1:8000/addserver">http://127.0.0.1:8000/addserver</a></p>
<p>显示 数据添加成功！</p>
<p>再次查询结果，可以看到新添加的记录</p>
<p><a target="_blank" rel="noopener" href="http://127.0.0.1:8000/show">http://127.0.0.1:8000/show</a></p>
<h2 id="Django-Web路由分发（可选）"><a href="#Django-Web路由分发（可选）" class="headerlink" title="Django Web路由分发（可选）"></a>Django Web路由分发（可选）</h2><p>之前所有的访问都集中在一个url.py中，不便管理。可以修改代码拆分url，添加路由分发功能：</p>
<p>主文件urls.py, 添加include</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path, include</span><br><span class="line"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> url</span><br><span class="line">urlpatterns = [</span><br><span class="line">    path(<span class="string">&#x27;admin/&#x27;</span>, admin.site.urls),</span><br><span class="line">    url(<span class="string">&#x27;hello&#x27;</span>, view.hello),</span><br><span class="line">    url(<span class="string">r&#x27;^person/&#x27;</span>, include(<span class="string">&quot;ServerMgr.urls&quot;</span>)),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>在ServerMgr中新建urls</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.conf.urls <span class="keyword">import</span> url</span><br><span class="line"><span class="keyword">from</span> ServerMgr <span class="keyword">import</span> views</span><br><span class="line">urlpatterns = [</span><br><span class="line">    url(<span class="string">r&#x27;^addname$&#x27;</span>, views.addname),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>

<p>浏览器访问：<br><a target="_blank" rel="noopener" href="http://127.0.0.1:8000/person/addname">http://127.0.0.1:8000/person/addname</a><br>也可以达到添加用户的功能。</p>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>重新生成表的方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">#删除 migrations&#x2F;0001_initial.py...</span><br><span class="line">#删除 django_migrations表中最新记录</span><br><span class="line">python manage.py makemigrations</span><br><span class="line">python manage.py migrate</span><br></pre></td></tr></table></figure>

<p>Django配置ibm_db 未测试</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">pip install ibm_db_django</span><br><span class="line"></span><br><span class="line"> DATABASES &#x3D; &#123;</span><br><span class="line">      &#39;default&#39;: &#123;</span><br><span class="line">         &#39;ENGINE&#39;     : &#39;ibm_db_django&#39;,</span><br><span class="line">         &#39;NAME&#39;       : &#39;mydb&#39;,</span><br><span class="line">         &#39;USER&#39;       : &#39;db2inst1&#39;,</span><br><span class="line">         &#39;PASSWORD&#39;   : &#39;ibmdb2&#39;,</span><br><span class="line">         &#39;HOST&#39;       : &#39;localhost&#39;,</span><br><span class="line">         &#39;PORT&#39;       : &#39;50000&#39;,</span><br><span class="line">         &#39;PCONNECT&#39;   :  True,      #Optional property, default is false</span><br><span class="line">      &#125;</span><br><span class="line">   &#125;</span><br></pre></td></tr></table></figure>

 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/协程，线程，进程是什么，在Python中怎么应用"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/%E5%8D%8F%E7%A8%8B%EF%BC%8C%E7%BA%BF%E7%A8%8B%EF%BC%8C%E8%BF%9B%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%8C%E5%9C%A8Python%E4%B8%AD%E6%80%8E%E4%B9%88%E5%BA%94%E7%94%A8/"
    >协程，线程，进程是什么，在Python中怎么应用.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/%E5%8D%8F%E7%A8%8B%EF%BC%8C%E7%BA%BF%E7%A8%8B%EF%BC%8C%E8%BF%9B%E7%A8%8B%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%8C%E5%9C%A8Python%E4%B8%AD%E6%80%8E%E4%B9%88%E5%BA%94%E7%94%A8/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="协程，线程，进程是什么，在Python中怎么应用？"><a href="#协程，线程，进程是什么，在Python中怎么应用？" class="headerlink" title="协程，线程，进程是什么，在Python中怎么应用？"></a>协程，线程，进程是什么，在Python中怎么应用？</h1><p>生活中的多任务时时刻刻存在，例如小张一边码字一边看屏幕，又例如小蔡可以一边跳舞一边打篮球，这就是生活中的多任务。那么计算机中的多任务是什么呢、怎么使用呢？就让我们一起探讨计算机中，多任务-线程、多任务-进程、多任务-协程的理解以及在Python中的应用。</p>
<p><strong>多任务</strong></p>
<p>多任务处理是指用户可以在同一时间内进行多种操作，每个操作被称作一个任务。在计算机中，同时打开迅雷以及QQ是多任务同时进行，在迅雷中看电影的时候，进行边下边播也是多任务，在同一时间同一单位进行的不同操作，都可以理解为多任务。</p>
<p>现在多核CPU已经非常普及了，但事实上，过去即便是单核CPU也可以执行多任务。由于CPU执行代码都是顺序执行的，那单核CPU是怎么执行多任务的呢？</p>
<p>答案就是操作系统轮流让各个任务交替执行，任务1执行0.01秒，切换到任务2，任务2执行0.01秒，再切换到任务3，……这样反复执行下去。表面上看，每个任务都是交替执行的，但是，由于CPU的执行速度实在是太快了，我们感觉就像所有任务都在同时执行。</p>
<p>真正实现并行执行多任务只能在多核CPU上实现，但往往任务数量远远多于CPU的核心数量，所以操作系统也会自动把很多任务轮流调度到每个核心上执行。在这里我们引入<strong>并发</strong>与<strong>并行</strong>以及<strong>队列</strong>的概念：</p>
<p><strong>并发：</strong></p>
<p>cpu通过在任务间快速切换达到多任务<strong>一起</strong>执行，但实际上并不是同时执行，举例：</p>
<p>A和B去跑步，跑道很挤只能容纳一个人，两人约定好每人跑一秒，大家都跑一秒就退出跑道。这时，在同一时间内，总有一个人在跑道内、一个人在跑道外（下图中两队人排同一个咖啡机即为并发）</p>
<p><strong>并行：</strong></p>
<p>每个任务都有不同cpu去执行,达到多任务一起执行，实际是真正的同时执行，举例还是A和B两人去跑步。这次跑道升级了，有两条跑道，A和B实现了并肩奔跑，你我互不影响（图中两队人排两台咖啡机即为并行）</p>
<p><strong>队列：</strong></p>
<p>就是一个有序的排列，在多任务中需要把待执行的任务排好队，有序执行。在A和B跑步的例子中，假设有20个A和20个B需要跑步，在排队等待跑步的时候，形成的排列就称为队列（图中两个队伍即为队列）</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172758834.webp" alt="img"></p>
<p>▲ 并发并行与队列</p>
<p>思考：迅雷播放电影的同时用QQ聊天，和在迅雷中看电影边下边播多任务有什么不同？</p>
<p>01</p>
<p><strong>线程</strong></p>
<ul>
<li>一个程序运行起来至少有一个进程，一个进程至少有一个线程；</li>
<li>处理器cpu分配给线程，即cpu真正运行的是线程中的代码；</li>
<li>分配cpu给线程时，是通过时间片轮训方式进行的；</li>
<li>进程是操作系统分配程序执行资源的单位，而线程是进程的一个实体；</li>
<li>是CPU调度和分配的单位。</li>
</ul>
<p>在上述思考中，迅雷和QQ属于不同的进程，迅雷下载电影和播放电影属于不同的线程，即一开始分配了两份资源给迅雷和QQ，迅雷和QQ各为一个进程。</p>
<p>当你打开迅雷边下边播功能的时候，在迅雷这个进程中又新开了两个线程，不断地在下载和播放间进行切换，达到多任务的效果.  线程与进程是属于关系。</p>
<p>线程由进程创建，进程结束线程也结束了，但线程结束进程不一定结束，cpu最终分配给的是线程，而不是进程。</p>
<p>线程执行代码片段原理：线程获得cpu执行内存，执行当前代码，在执行另一个代码块之前打上时间戳，存储上下文然后去执行另一代码块。当再次回到该代码块时加载时间戳，上下文，验证执行的合理性，如此反复执行下去，在不同的需要执行的代码块间切换。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748848.webp" alt="img"></p>
<p>▲ 进程与线程间的关系</p>
<p><strong>子线程何时开启，何时运行？</strong></p>
<p>当调用thread.start()时 开启线程，再运行线程的代码。</p>
<p><strong>子线程何时结束？</strong></p>
<p>子线程把target指向的函数中的语句执行完毕后，或者线程中的run函数代码执行完毕后，立即结束当前子线程。</p>
<p><strong>查看当前线程数量</strong></p>
<p>通过threading.enumerate()可枚举当前运行的所有线程。</p>
<p><strong>主线程何时结束？</strong></p>
<p>所有子线程执行完毕后，主线程才结束。</p>
<p>02</p>
<p><strong>进程</strong></p>
<p><strong>进程：</strong></p>
<p>一个程序运行起来后，“代码+用到的资源”称之为进程，它是操作系统分配资源的基本单位。</p>
<p><strong>进程状态：</strong></p>
<p>工作中，任务数往往大于cpu的核数，即一定有一些任务正在执行，另外一些任务在等待cpu进行执行，因此导致了有了不同的状态。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748983.webp" alt="img"></p>
<p><strong>就绪态：</strong></p>
<p>运行的条件都已经满足，正在等在cpu执行。</p>
<p><strong>执行态：</strong></p>
<p>cpu正在执行其功能。</p>
<p><strong>等待态：</strong></p>
<p>等待某些条件满足，例如一个程序sleep了，此时就处于等待态，好比说：红绿灯、等待消息回复、等待同步锁都是处于等待态。</p>
<p>03</p>
<p><strong>协程</strong></p>
<p>协程是Python中另外一种实现多任务的方式，只不过比线程更小占用、执行单元，由于协程是本世纪出现的新概念，所以对于协程来说没有统一的概念，这里介绍我自己的理解，<strong>协程相当于更便捷更轻量的线程</strong>。</p>
<p>协程与线程差异在于，实现多任务时, 线程切换从系统层面远不止保存和恢复 CPU上下文这么简单。操作系统为了程序运行的高效性，每个线程都有自己缓存Cache等数据，操作系统还会帮你做这些数据的恢复操作。所以线程的切换非常耗性能。但是协程的切换只是单纯操作CPU的上下文，所以一秒钟切换个上百万次系统都扛得住。</p>
<p>04</p>
<p><strong>队列与他们的关系</strong></p>
<p>三者在工作时都需要取得cpu，为了避免彼此之间争夺cpu，所以需要对他们进行排队处理，排好的队伍就叫队列，例如线程池、进程池。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172749149.png" alt="img"></p>
<p>▲ 队列的原理</p>
<p>05</p>
<p><strong>三者间的关系</strong></p>
<p>进程&gt;线程&gt;协程</p>
<p>线程由进程创建，属于进程，协程是进程更小程度的划分，更轻便灵活，如下图：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748987.webp" alt="img"></p>
<p>▲ 进程线程协程三者间的关系</p>
<p><strong>在Python中实现多任务</strong></p>
<p>01</p>
<p><strong>Python实现多线程</strong></p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748990.webp" alt="img"></p>
<ul>
<li>自定义类，继承threading.Thread；</li>
<li>创建对象；</li>
<li>调用对象的run()方法。</li>
</ul>
<p>02</p>
<p><strong>Python实现多进程</strong></p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748988.webp" alt="img"></p>
<ul>
<li>实例化一个对象 target= 指定到对应的函数；</li>
<li>调用对象的run()方法。</li>
</ul>
<p>03</p>
<p><strong>Python实现多协程</strong></p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748987.webp" alt="img"></p>
<ul>
<li>函数中调用yield；</li>
<li>调用yield后函数会在执行到调用send() 方法结果返回时才继续进行下一步；</li>
<li>执行函数，函数会交替执行。</li>
</ul>
<p><strong>多任务的痛点及解决方法</strong></p>
<p>01</p>
<p><strong>痛点1</strong></p>
<p>如果多个线程同时对一个全局变量操作，会出现资源竞争问题，从而数据结果会不正确。</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748915.webp" alt="img"></p>
<p>运行上述代码后会发现两个线程对同一个数据操作完后得到的数据不一样，这就是遇到了线程安全问题。</p>
<p><strong>解决方法：</strong></p>
<p>同步就是协同步调，按预定的先后次序运行。如：你说完，我再说；你做完，我再做；你执行完，我再执行。</p>
<p>在多线程编程中，一些敏感数据不允许被多个线程同时访问，因为会出现线程安全问题。通过线程同步机制，能保证共享数据在任何时刻，最多有一个线程访问，以保证数据的正确性。线程同步提示的几点：</p>
<ol>
<li>线程同步就是线程排队；</li>
<li>共享资源的读写才需要同步；</li>
<li>变量才需要同步，常量不需要同步；</li>
<li>给数据加锁,即我操作完你再操作,你操作完我再操作。</li>
</ol>
<p>threading模块中定义了Lock类，可以方便的处理锁定：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748859.webp" alt="img"></p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748918.webp" alt="img"></p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748989.webp" alt="img"></p>
<p>02</p>
<p><strong>痛点2</strong></p>
<p>GIL全局解释器锁：顾名思义，这是解释器内部的一把锁，确切一点说是CPython解释器内部的一把锁，所以要注意区分我们在Python代码中使用的Lock不是一个层面的概念。言外之意，就是全局解释器就是为了锁定整个解释器内部的全局资源，每个线程想要运行首先获取GIL，而GIL本身又是一把互斥锁，造成所有线程只能一个一个one-by-one-并发-交替的执行。</p>
<p>也就是说Python中多线程并不能很好的实现并发操作，但Python恰好又是实现多协程的一种方法，所以对于Python来说，实现多任务最好的方式即为<strong>多进程+多**</strong>协程**。</p>
<p><strong>扩展 celery原理</strong></p>
<p>celery是基于Python实现的一个异步任务的调度工具，同时还是一个任务队列，主要用于处理耗时的任务。</p>
<p>大家在使用celery的时候，都需要去配置一个队列才能继续使用，因为对于celery来说，也是一个生产者消费者的模式，我们一般使用的队列是Redis或者RabbitMQ，因为存储格式为键值对形式，序号对应任务，利于cpu执行。celery即为消息中间件，任务执行单元，任务执行结果储存的形式进行异步操作，如图：</p>
<p><img src="http://iubest.gitee.io/pic/640-1601172748989.webp" alt="img"></p>
<p><strong>总结</strong></p>
<p>请看如下例子: 有一个老板想要开个工厂进行生产剪子，他需要花一些财力物力制作一条生产线，这个生产线上有很多的器件以及材料这些所有的，为了能够生产剪子而准备的资源称之为：<strong>进程</strong></p>
<p>只有生产线是不能够进行生产的，所以老板的找个工人来进行生产，这个工人能够利用这些材料最终一步步的将剪子做出来，这个来做事情的工人称之为：<strong>线程</strong></p>
<p>这个老板为了提高生产率，想到3种办法：</p>
<ul>
<li>在这条生产线上多招些工人，一起来做剪子，这样效率是成倍増长，即单进程 多线程方式。</li>
</ul>
<ul>
<li>老板发现这条生产线上的工人不是越多越好，因为一条生产线的资源以及材料毕竟有限，所以老板又花了些财力物力购置了另外一条生产线，然后再招些工人这样效率又再一步提高了，即多进程 多线程方式。</li>
</ul>
<ul>
<li>老板发现，现在已经有了很多条生产线，并且每条生产线上已经有很多工人了（即程序是多进程的，每个进程中又有多个线程），为了再次提高效率，老板想了个损招，规定：如果某个员工在上班时临时没事或者再等待某些条件（比如等待另一个工人生产完某道工序 之后他才能再次工作） ，那么这个员工就利用这个时间去做其它的事情，那么也就是说：如果一个线程等待某些条件，可以充分利用这个时间去做其它事情，其实这就是：协程方式。</li>
</ul>
<p>简单来说：</p>
<ol>
<li>进程是操作系统资源分配的单位；</li>
<li>线程是CPU调度的单位；</li>
<li>进程切换需要的资源最大，效率很低；</li>
<li>线程切换需要的资源一般，效率一般（当然在不考虑GIL的情况下）；</li>
<li>协程切换任务资源很小，效率高；</li>
<li>多进程、多线程根据cpu核数不一样可能是并行的，但是协程是在一个线程中所以是并发。</li>
</ol>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
    <article
  id="post-develop/在Python中使用httpx模块"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2020/11/11/develop/%E5%9C%A8Python%E4%B8%AD%E4%BD%BF%E7%94%A8httpx%E6%A8%A1%E5%9D%97/"
    >在Python中使用httpx模块.md</a> 
</h2>
 

    </header>
     
    <div class="article-meta">
      <a href="/2020/11/11/develop/%E5%9C%A8Python%E4%B8%AD%E4%BD%BF%E7%94%A8httpx%E6%A8%A1%E5%9D%97/" class="article-date">
  <time datetime="2020-11-10T16:00:00.000Z" itemprop="datePublished">2020-11-11</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/develop/">develop</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h1 id="在Python中使用httpx模块"><a href="#在Python中使用httpx模块" class="headerlink" title="在Python中使用httpx模块"></a>在Python中使用httpx模块</h1><h3 id="导入httpx"><a href="#导入httpx" class="headerlink" title="导入httpx"></a>导入<code>httpx</code></h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">In [25]: import httpx  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>获取一个网页</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">In [26]: r &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;get&quot;)  </span><br><span class="line">  </span><br><span class="line">In [27]: r  </span><br><span class="line">Out[27]: &lt;Response [200 OK]&gt;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>同样,发送<code>HTTP POST</code>请求:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">In [28]: r &#x3D; httpx.post(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;, data&#x3D;&#123;&quot;key&quot;: &quot;value&quot;&#125;)  </span><br><span class="line">  </span><br><span class="line">In [29]: r  </span><br><span class="line">Out[29]: &lt;Response [200 OK]&gt;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>PUT,DELETE,HEAD和OPTIONS请求都遵循相同的方式:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">In [35]: r &#x3D; httpx.put(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;put&quot;, data&#x3D;&#123;&quot;key&quot;: &quot;value&quot;&#125;)  </span><br><span class="line">  </span><br><span class="line">In [36]: r &#x3D; httpx.delete(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;delete&quot;)  </span><br><span class="line">  </span><br><span class="line">In [37]: r &#x3D; httpx.head(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;get&quot;)  </span><br><span class="line">  </span><br><span class="line">In [38]: r &#x3D; httpx.options(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;get&quot;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="在URL中传递参数"><a href="#在URL中传递参数" class="headerlink" title="在URL中传递参数"></a>在<code>URL</code>中传递参数</h3><ol>
<li>在请求URL中传递查询参数,请使用<code>params</code>关键字</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">In [41]: r &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;get&quot;, params&#x3D;params)  </span><br><span class="line">  </span><br><span class="line">In [42]: r  </span><br><span class="line">Out[42]: &lt;Response [200 OK]&gt;  </span><br><span class="line">  </span><br><span class="line"># 检查发出请求结果的&#96;URL&#96;  </span><br><span class="line">In [43]: r.url  </span><br><span class="line">Out[43]: URL(&#39;https:&#x2F;&#x2F;httpbin.org&#x2F;get?key1&#x3D;value2&amp;key2&#x3D;value2&#39;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ol start="2">
<li>将项目列表作为值传递</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">In [44]: params &#x3D; &#123;&quot;key1&quot;: &quot;value2&quot;, &quot;key2&quot;: [&quot;value2&quot;, &quot;value3&quot;]&#125;  </span><br><span class="line">  </span><br><span class="line">In [45]: r &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;get&quot;, params&#x3D;params)  </span><br><span class="line">  </span><br><span class="line">In [46]: r.url  </span><br><span class="line">Out[46]: URL(&#39;https:&#x2F;&#x2F;httpbin.org&#x2F;get?key1&#x3D;value2&amp;key2&#x3D;value2&amp;key2&#x3D;value3&#39;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="响应内容"><a href="#响应内容" class="headerlink" title="响应内容"></a>响应内容</h3><p>HTTPX将自动处理响应内容解码为<code>Unicode</code>文本</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">In [52]: r &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;www.example.org&#x2F;&quot;)  </span><br><span class="line">  </span><br><span class="line">In [53]: r.text  </span><br><span class="line">Out[53]: &#39;&lt;!doctype html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;Example Domain&lt;&#x2F;title&gt;\n\n    &lt;meta charset&#x3D;&quot;utf-8&quot; &#x2F;&gt;\n    &lt;meta http-equiv&#x3D;&quot;Content-type&quot; content&#x3D;&quot;text&#x2F;html; charset&#x3D;utf-8&quot; &#x2F;&gt;\n    &lt;meta  &#x2F;&gt;\n    &lt;style type&#x3D;&quot;text&#x2F;css&quot;&gt;\n    body &#123;\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: -apple-system, system-ui, BlinkMacSystemFont, &quot;Segoe UI&quot;, &quot;Open Sans&quot;, &quot;Helvetica Neue&quot;, Helvetica, Arial, sans-serif;\n        \n    &#125;\n    div &#123;\n        width: 600px;\n        margin: 5em auto;\n        padding: 2em;\n        background-color: #fdfdff;\n        border-radius: 0.5em;\n        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n    &#125;\n    a:link, a:visited &#123;\n        color: #38488f;\n        text-decoration: none;\n    &#125;\n    @media (max-width: 700px) &#123;\n        div &#123;\n            margin: 0 auto;\n            width: auto;\n        &#125;\n    &#125;\n    &lt;&#x2F;style&gt;    \n&lt;&#x2F;head&gt;\n\n&lt;body&gt;\n&lt;div&gt;\n    &lt;h1&gt;Example Domain&lt;&#x2F;h1&gt;\n    &lt;p&gt;This domain is for use in illustrative examples in documents. You may use this\n    domain in literature without prior coordination or asking for permission.&lt;&#x2F;p&gt;\n    &lt;p&gt;&lt;a href&#x3D;&quot;https:&#x2F;&#x2F;www.iana.org&#x2F;domains&#x2F;example&quot;&gt;More information...&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;\n&lt;&#x2F;div&gt;\n&lt;&#x2F;body&gt;\n&lt;&#x2F;html&gt;\n&#39;  </span><br><span class="line">  </span><br><span class="line"># 解码  </span><br><span class="line">In [54]: r.encoding  </span><br><span class="line">Out[54]: &#39;UTF-8&#39;  </span><br><span class="line">  </span><br><span class="line"># 设置需要使用的编码  </span><br><span class="line">In [55]: r.encoding &#x3D; &quot;ISO-8859-1&quot;  </span><br><span class="line">  </span><br><span class="line">In [56]: print(r.headers, r.http_version, r.url, r.status_code)  </span><br><span class="line">Headers(&#123;&#39;content-encoding&#39;: &#39;gzip&#39;, &#39;age&#39;: &#39;532553&#39;, &#39;cache-control&#39;: &#39;max-age&#x3D;604800&#39;, &#39;content-type&#39;: &#39;text&#x2F;html; charset&#x3D;UTF-8&#39;, &#39;date&#39;: &#39;Sat, 06 Jun 2020 04:22:26 GMT&#39;, &#39;etag&#39;: &#39;&quot;3147526947+gzip&quot;&#39;, &#39;expires&#39;: &#39;Sat, 13 Jun 2020 04:22:26 GMT&#39;, &#39;last-modified&#39;: &#39;Thu, 17 Oct 2019 07:18:26 GMT&#39;, &#39;server&#39;: &#39;ECS (oxr&#x2F;830C)&#39;, &#39;vary&#39;: &#39;Accept-Encoding&#39;, &#39;x-cache&#39;: &#39;HIT&#39;, &#39;content-length&#39;: &#39;648&#39;&#125;) HTTP&#x2F;1.1 https:&#x2F;&#x2F;www.example.org&#x2F; 200  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="返回JSON响应内容"><a href="#返回JSON响应内容" class="headerlink" title="返回JSON响应内容"></a>返回JSON响应内容</h3><p>通常, Web API 响应将被编码为JSON</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">In [59]: r &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;api.github.com&#x2F;events&quot;)  </span><br><span class="line">In [60]: r.json()  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="自定义header"><a href="#自定义header" class="headerlink" title="自定义header"></a>自定义<code>header</code></h3><p>要在传出请求中包含其他标头,请使用<code>header</code>关键字参数:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">In [60]: url &#x3D; &#39;http:&#x2F;&#x2F;httpbin.org&#x2F;headers&#39;  </span><br><span class="line">  </span><br><span class="line">In [61]: headers &#x3D; &#123;&quot;user-agent&quot;: &quot;my-app&#x2F;0.0.1&quot;&#125;  </span><br><span class="line">  </span><br><span class="line">In [62]: r &#x3D; httpx.get(url, headers&#x3D;headers)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="发送表单数据"><a href="#发送表单数据" class="headerlink" title="发送表单数据"></a>发送表单数据</h3><p>某些类型的HTTP请求,如<code>POST|PUT</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">In [64]: data &#x3D; &#123;&quot;key1&quot;:&quot;value1&quot;, &quot;key1&quot;: &quot;value2&quot;&#125;  </span><br><span class="line">  </span><br><span class="line">In [65]: r &#x3D; httpx.post(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;, data&#x3D;data)  </span><br><span class="line">  </span><br><span class="line">In [66]: print(r.text)  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;args&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;data&quot;: &quot;&quot;,  </span><br><span class="line">  &quot;files&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;form&quot;: &#123;  </span><br><span class="line">    &quot;key1&quot;: &quot;value2&quot;  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;headers&quot;: &#123;  </span><br><span class="line">    &quot;Accept&quot;: &quot;*&#x2F;*&quot;,  </span><br><span class="line">    &quot;Accept-Encoding&quot;: &quot;gzip, deflate&quot;,  </span><br><span class="line">    &quot;Content-Length&quot;: &quot;11&quot;,  </span><br><span class="line">    &quot;Content-Type&quot;: &quot;application&#x2F;x-www-form-urlencoded&quot;,  </span><br><span class="line">    &quot;Host&quot;: &quot;httpbin.org&quot;,  </span><br><span class="line">    &quot;User-Agent&quot;: &quot;python-httpx&#x2F;0.12.1&quot;,  </span><br><span class="line">    &quot;X-Amzn-Trace-Id&quot;: &quot;Root&#x3D;1-5edb1e61-86ac64e4f9f165244f4dbd68&quot;  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;json&quot;: null,  </span><br><span class="line">  &quot;origin&quot;: &quot;114.113.113.186&quot;,  </span><br><span class="line">  &quot;url&quot;: &quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;  </span><br><span class="line">&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>表单编码的数据还可以包括给定键的多个值:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">In [72]: data &#x3D; &#123;&quot;userList&quot;: [&quot;shuke&quot;, &quot;jack&quot;]&#125;  </span><br><span class="line">  </span><br><span class="line">In [73]: r &#x3D; httpx.post(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;, data&#x3D;data)  </span><br><span class="line">  </span><br><span class="line">In [74]: print(r.text)  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;args&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;data&quot;: &quot;&quot;,  </span><br><span class="line">  &quot;files&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;form&quot;: &#123;  </span><br><span class="line">    &quot;userList&quot;: [  </span><br><span class="line">      &quot;shuke&quot;,  </span><br><span class="line">      &quot;jack&quot;  </span><br><span class="line">    ]  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;headers&quot;: &#123;  </span><br><span class="line">    &quot;Accept&quot;: &quot;*&#x2F;*&quot;,  </span><br><span class="line">    &quot;Accept-Encoding&quot;: &quot;gzip, deflate&quot;,  </span><br><span class="line">    &quot;Content-Length&quot;: &quot;28&quot;,  </span><br><span class="line">    &quot;Content-Type&quot;: &quot;application&#x2F;x-www-form-urlencoded&quot;,  </span><br><span class="line">    &quot;Host&quot;: &quot;httpbin.org&quot;,  </span><br><span class="line">    &quot;User-Agent&quot;: &quot;python-httpx&#x2F;0.12.1&quot;,  </span><br><span class="line">    &quot;X-Amzn-Trace-Id&quot;: &quot;Root&#x3D;1-5edb1fc2-33761faf662f0b832cb806bb&quot;  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;json&quot;: null,  </span><br><span class="line">  &quot;origin&quot;: &quot;114.113.113.186&quot;,  </span><br><span class="line">  &quot;url&quot;: &quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;  </span><br><span class="line">&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>发送分段文件上传</p>
<p>使用HTTP分段编码上传文件</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">In [76]: files &#x3D; &#123;&quot;upload-file&quot;: open(&quot;&#x2F;Users&#x2F;shuke&#x2F;Work&#x2F;pha&#x2F;docker-compose.yml&quot;, &quot;rb&quot;)&#125;  </span><br><span class="line">  </span><br><span class="line">In [77]: r &#x3D; httpx.post(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;, files&#x3D;files)  </span><br><span class="line">  </span><br><span class="line">In [78]: print(r.text)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>发送JSON编码数据</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">In [79]: data &#x3D; &#123;&quot;integer&quot;: 123, &quot;boolean&quot;: True, &quot;list&quot;: [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;]&#125;  </span><br><span class="line">  </span><br><span class="line">In [80]: r &#x3D; httpx.post(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;, json&#x3D;data)  </span><br><span class="line">  </span><br><span class="line">In [81]: print(r.text)  </span><br><span class="line">&#123;  </span><br><span class="line">  &quot;args&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;data&quot;: &quot;&#123;\&quot;integer\&quot;: 123, \&quot;boolean\&quot;: true, \&quot;list\&quot;: [\&quot;a\&quot;, \&quot;b\&quot;, \&quot;c\&quot;]&#125;&quot;,  </span><br><span class="line">  &quot;files&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;form&quot;: &#123;&#125;,  </span><br><span class="line">  &quot;headers&quot;: &#123;  </span><br><span class="line">    &quot;Accept&quot;: &quot;*&#x2F;*&quot;,  </span><br><span class="line">    &quot;Accept-Encoding&quot;: &quot;gzip, deflate&quot;,  </span><br><span class="line">    &quot;Content-Length&quot;: &quot;58&quot;,  </span><br><span class="line">    &quot;Content-Type&quot;: &quot;application&#x2F;json&quot;,  </span><br><span class="line">    &quot;Host&quot;: &quot;httpbin.org&quot;,  </span><br><span class="line">    &quot;User-Agent&quot;: &quot;python-httpx&#x2F;0.12.1&quot;,  </span><br><span class="line">    &quot;X-Amzn-Trace-Id&quot;: &quot;Root&#x3D;1-5edb217c-cd09ef8caa55514051957eb8&quot;  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;json&quot;: &#123;  </span><br><span class="line">    &quot;boolean&quot;: true,  </span><br><span class="line">    &quot;integer&quot;: 123,  </span><br><span class="line">    &quot;list&quot;: [  </span><br><span class="line">      &quot;a&quot;,  </span><br><span class="line">      &quot;b&quot;,  </span><br><span class="line">      &quot;c&quot;  </span><br><span class="line">    ]  </span><br><span class="line">  &#125;,  </span><br><span class="line">  &quot;origin&quot;: &quot;114.113.113.186&quot;,  </span><br><span class="line">  &quot;url&quot;: &quot;https:&#x2F;&#x2F;httpbin.org&#x2F;post&quot;  </span><br><span class="line">&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="响应状态码"><a href="#响应状态码" class="headerlink" title="响应状态码"></a>响应状态码</h3><p>检查响应的<code>HTTP</code>状态码</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">In [82]: r &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;get&quot;)  </span><br><span class="line">  </span><br><span class="line">In [83]: r.status_code  </span><br><span class="line">Out[83]: 200  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>HTTPX还包括一个简单的快捷方式,用于通过其文本短语访问状态代码</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">In [84]: r.status_code &#x3D;&#x3D; httpx.codes.OK  </span><br><span class="line">Out[84]: True  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>针对任何客户端或服务器错误响应(4xx或5xx状态代码) 引发异常:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">In [85]: not_found &#x3D; httpx.get(&quot;https:&#x2F;&#x2F;httpbin.org&#x2F;status&#x2F;404&quot;)  </span><br><span class="line">  </span><br><span class="line">In [86]: not_found.status_code  </span><br><span class="line">Out[86]: 404  </span><br><span class="line">  </span><br><span class="line">In [87]: not_found.rasise_for_status()  </span><br><span class="line">---------------------------------------------------------------------------  </span><br><span class="line">AttributeError                            Traceback (most recent call last)  </span><br><span class="line">&lt;ipython-input-87-c7b57e835640&gt; in &lt;module&gt;  </span><br><span class="line">----&gt; 1 not_found.rasise_for_status()  </span><br><span class="line">  </span><br><span class="line">AttributeError: &#39;Response&#39; object has no attribute &#39;rasise_for_status&#39;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="响应Headers"><a href="#响应Headers" class="headerlink" title="响应Headers"></a>响应Headers</h3><p>响应标头可作为类似于字典的接口使用</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">In [88]: r.headers  </span><br><span class="line">Out[88]: Headers(&#123;&#39;date&#39;: &#39;Sat, 06 Jun 2020 04:56:56 GMT&#39;, &#39;content-type&#39;: &#39;application&#x2F;json&#39;, &#39;content-length&#39;: &#39;306&#39;, &#39;connection&#39;: &#39;keep-alive&#39;, &#39;server&#39;: &#39;gunicorn&#x2F;19.9.0&#39;, &#39;access-control-allow-origin&#39;: &#39;*&#39;, &#39;access-control-allow-credentials&#39;: &#39;true&#39;&#125;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="该Headers数据类型是不区分大小写的，所以你可以使用任何资本"><a href="#该Headers数据类型是不区分大小写的，所以你可以使用任何资本" class="headerlink" title="该Headers数据类型是不区分大小写的，所以你可以使用任何资本"></a>该<code>Headers</code>数据类型是不区分大小写的，所以你可以使用任何资本</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">In [89]: r.headers[&quot;Content-Type&quot;]  </span><br><span class="line">Out[89]: &#39;application&#x2F;json&#39;  </span><br><span class="line">  </span><br><span class="line">In [90]: r.headers.get(&quot;content-type&quot;)  </span><br><span class="line">Out[90]: &#39;application&#x2F;json&#39;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="流响应"><a href="#流响应" class="headerlink" title="流响应"></a>流响应</h3><blockquote>
<p>可以流式传输响应的二进制内容</p>
</blockquote>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">In [91]: with httpx.stream(&quot;GET&quot;, &quot;https:&#x2F;&#x2F;www.example.com&quot;) as r:  </span><br><span class="line">    ...:     for data in r.iter_bytes():  </span><br><span class="line">    ...:         print(data)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>或文字…</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">In [93]: with httpx.stream(&quot;GET&quot;, &quot;https:&#x2F;&#x2F;www.example.com&quot;) as r:  </span><br><span class="line">    ...:     for text in r.iter_text():  </span><br><span class="line">    ...:         print(text)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>或逐行流文本…</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">In [96]: with httpx.stream(&quot;GET&quot;, &quot;https:&#x2F;&#x2F;www.example.com&quot;) as r:  </span><br><span class="line">    ...:     for text in r.iter_lines():  </span><br><span class="line">    ...:         print(text)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="Cookies"><a href="#Cookies" class="headerlink" title="Cookies"></a>Cookies</h3><p>可以轻松访问响应中设置的任何cookie:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">In [97]: r &#x3D; httpx.get(&quot;http:&#x2F;&#x2F;httpbin.org&#x2F;cookies&#x2F;set?chocolate&#x3D;chip&quot;, allow_redirects&#x3D;False)  </span><br><span class="line">  </span><br><span class="line">In [98]: r.cookies[&quot;chocolate&quot;]  </span><br><span class="line">Out[98]: &#39;chip&#39;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>如果需要将<code>Cookies</code>包含在外发请求中,请使用<code>cookies</code>参数:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">In [99]: cookies &#x3D; &#123;&quot;peanut&quot;: &quot;butter&quot;&#125;  </span><br><span class="line">  </span><br><span class="line">In [100]: r &#x3D; httpx.get(&quot;http:&#x2F;&#x2F;httpbin.org&#x2F;cookies&quot;, cookies&#x3D;cookies)  </span><br><span class="line">  </span><br><span class="line">In [101]: r.json()  </span><br><span class="line">Out[101]: &#123;&#39;cookies&#39;: &#123;&#39;peanut&#39;: &#39;butter&#39;&#125;&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><code>Cookies</code> 按 域访问设置</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">In [102]: cookies &#x3D; httpx.Cookies()  </span><br><span class="line">  </span><br><span class="line">In [103]: cookies.set(&#39;cookie_on_domain&#39;, &#39;hello, there!&#39;, domain&#x3D;&#39;httpbin.org&#39;)  </span><br><span class="line">  </span><br><span class="line">In [104]: cookies.set(&#39;cookies_off_domain&#39;, &#39;nope&#39;, domain&#x3D;&quot;example.org&quot;)  </span><br><span class="line">  </span><br><span class="line">In [105]: r &#x3D; httpx.get(&quot;http:&#x2F;&#x2F;httpbin.org&#x2F;cookies&quot;, cookies&#x3D;cookies)  </span><br><span class="line">  </span><br><span class="line">In [106]: r.json()  </span><br><span class="line">Out[106]: &#123;&#39;cookies&#39;: &#123;&#39;cookie_on_domain&#39;: &#39;hello, there!&#39;&#125;&#125;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><code>URL</code> 重定向和历史 默认情况下,HTTPX将对重定向执行除<code>HEAD</code>请求之外的任何操作。<code>history</code>响应的属性可用于检查所有后续重定向,它包含遵循他们的顺序的所有重定向响应的列表<code>GITHUB</code>将所有<code>HTTP</code>请求重定向到<code>HTTPS</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">In [107]: r &#x3D; httpx.get(&quot;http:&#x2F;&#x2F;github.com&quot;)  </span><br><span class="line">  </span><br><span class="line">In [108]: r.url  </span><br><span class="line">Out[108]: URL(&#39;https:&#x2F;&#x2F;github.com&#39;)  </span><br><span class="line">  </span><br><span class="line">In [109]: r.status_code  </span><br><span class="line">Out[109]: 200  </span><br><span class="line">  </span><br><span class="line">In [110]: r.history  </span><br><span class="line">Out[110]: []  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>您可以使用allow_redirects参数修改默认的重定向处理:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">In [113]: r &#x3D; httpx.head(&#39;http:&#x2F;&#x2F;github.com&#x2F;&#39;, allow_redirects&#x3D;True)  </span><br><span class="line">  </span><br><span class="line">In [114]: r.url  </span><br><span class="line">Out[114]: URL(&#39;https:&#x2F;&#x2F;github.com&#x2F;&#39;)  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h3 id="认证方式"><a href="#认证方式" class="headerlink" title="认证方式"></a>认证方式</h3><ol>
<li>HTTPX支持基本和摘要HTTP身份验证</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">In [122]: httpx.get(&quot;https:&#x2F;&#x2F;example.com&quot;, auth&#x3D;(&quot;my_user&quot;, &quot;password123&quot;))  </span><br><span class="line">     ...:  </span><br><span class="line">Out[122]: &lt;Response [200 OK]&gt;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<ol start="2">
<li>摘要式身份验证的凭据</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">In [123]: auth &#x3D; httpx.DigestAuth(&quot;my_user&quot;, &quot;password123&quot;)  </span><br><span class="line">  </span><br><span class="line">In [124]: httpx.get(&quot;https:&#x2F;&#x2F;example.com&quot;, auth&#x3D;auth)  </span><br><span class="line">Out[124]: &lt;Response [200 OK]&gt;  </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><img src=""></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/develop/" rel="tag">develop</a></li></ul>

    </footer>
  </div>

    
 
   
</article>

    
  </article>
  

  
  <nav class="page-nav">
    
    <a class="extend prev" rel="prev" href="/">上一页</a><a class="page-number" href="/">1</a><span class="page-number current">2</span><a class="page-number" href="/page/3/">3</a><a class="page-number" href="/page/4/">4</a><span class="space">&hellip;</span><a class="page-number" href="/page/14/">14</a><a class="extend next" rel="next" href="/page/3/">下一页</a>
  </nav>
  
</section>
</div>

      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2015-2020
        <i class="ri-heart-fill heart_icon"></i> TzWind
      </li>
    </ul>
    <ul>
      <li>
        
        
        
        由 <a href="https://hexo.io" target="_blank">Hexo</a> 强力驱动
        <span class="division">|</span>
        主题 - <a href="https://github.com/Shen-Yu/hexo-theme-ayer" target="_blank">Ayer</a>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></s>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>
      <div class="float_btns">
        <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

      </div>
    </main>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/ayer-side.svg" alt="Hexo"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" target="_blank" rel="noopener" href="http://www.baidu.com">百度</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/friends">友链</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/2019/about">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
      <a class="nav-item-link" target="_blank" href="/atom.xml" title="RSS Feed">
        <i class="ri-rss-line"></i>
      </a>
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <script>
      if (window.matchMedia("(max-width: 768px)").matches) {
        document.querySelector('.content').classList.remove('on');
        document.querySelector('.sidebar').classList.remove('on');
      }
    </script>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝杯咖啡吧~</p>
  <div class="reward-box">
    
    
  </div>
</div>
    
<script src="/js/jquery-2.0.3.min.js"></script>


<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css">
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->

<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script>

<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->


<script src="/js/busuanzi-2.3.pure.min.js"></script>


<!-- ClickLove -->

<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->


<link rel="stylesheet" href="/css/clipboard.css">

<script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>


<!-- CanvasBackground -->


    
  </div>
</body>

</html>